Philipp Hörist pushed to branch master at gajim / gajim
Commits:
eebbe3e8 by wurstsalat at 2022-11-30T21:34:12+00:00
refactor: Remove old emoji code
Fixes #10932
- - - - -
23 changed files:
- gajim/common/configpaths.py
- gajim/common/helpers.py
- gajim/common/setting_values.py
- gajim/common/settings.py
- gajim/common/styling.py
- − gajim/data/emoticons/noto/LICENSE
- − gajim/data/emoticons/noto/noto.png
- − gajim/data/emoticons/twemoji/LICENSE
- − gajim/data/emoticons/twemoji/twemoji.png
- gajim/data/gui/message_actions_box.ui
- gajim/data/gui/preferences.ui
- gajim/data/style/gajim.css
- gajim/gtk/application.py
- gajim/gtk/chat_stack.py
- gajim/gtk/conversation/plain_widget.py
- − gajim/gtk/emoji_chooser.py
- − gajim/gtk/emoji_data.py
- − gajim/gtk/emoji_data.pyi
- gajim/gtk/message_actions_box.py
- gajim/gtk/message_input.py
- gajim/gtk/preferences.py
- pyproject.toml
- setup.cfg
Changes:
=====================================
gajim/common/configpaths.py
=====================================
@@ -125,7 +125,6 @@ def __init__(self) -> None:
source_paths = [
('DATA', basedir / 'data'),
('STYLE', basedir / 'data' / 'style'),
- ('EMOTICONS', basedir / 'data' / 'emoticons'),
('GUI', basedir / 'data' / 'gui'),
('ICONS', basedir / 'data' / 'icons'),
('HOME', Path.home()),
@@ -216,8 +215,6 @@ def init(self):
('PLUGINS_IMAGES', 'plugins_images',
PathLocation.CACHE, PathType.FOLDER),
('PLUGINS_USER', 'plugins', PathLocation.DATA, PathType.FOLDER),
- ('MY_EMOTS',
- 'emoticons', PathLocation.DATA, PathType.FOLDER_OPTIONAL),
('MY_ICONSETS',
'iconsets', PathLocation.DATA, PathType.FOLDER_OPTIONAL),
=====================================
gajim/common/helpers.py
=====================================
@@ -766,22 +766,6 @@ def version_condition(current_version: str, required_version: str) -> bool:
return True
-def get_available_emoticon_themes() -> list[str]:
- files: list[Path] = []
- for folder in configpaths.get('EMOTICONS').iterdir():
- if not folder.is_dir():
- continue
- files += [theme for theme in folder.iterdir() if theme.is_file()]
-
- my_emots = configpaths.get('MY_EMOTS')
- if my_emots.is_dir():
- files += list(my_emots.iterdir())
-
- emoticons_themes = ['font']
- emoticons_themes += [file.stem for file in files if file.suffix == '.png']
- return sorted(emoticons_themes)
-
-
def call_counter(func):
def helper(self, restart=False):
if restart:
=====================================
gajim/common/setting_values.py
=====================================
@@ -49,7 +49,6 @@ class _ACCOUNT_DEFAULT:
'confirm_close_muc',
'confirm_on_window_delete',
'dev_force_bookmark_2',
- 'dev_use_message_label',
'developer_modus',
'enable_emoji_shortcodes',
'enable_negative_priority',
@@ -137,7 +136,6 @@ class _ACCOUNT_DEFAULT:
'date_format',
'date_time_format',
'dictionary_url',
- 'emoticons_theme',
'gc_refer_to_nick_char',
'global_proxy',
'last_save_dir',
@@ -194,7 +192,6 @@ class _ACCOUNT_DEFAULT:
'dev_use_message_label': True,
'developer_modus': False,
'dictionary_url': 'WIKTIONARY',
- 'emoticons_theme': 'noto-emoticons',
'enable_emoji_shortcodes': True,
'enable_negative_priority': False,
'enable_file_preview': True,
=====================================
gajim/common/settings.py
=====================================
@@ -237,11 +237,6 @@ def _setup_installation_defaults() -> None:
if IS_PORTABLE:
APP_SETTINGS['use_keyring'] = False
- if sys.platform in ('win32', 'darwin'):
- # Cairo does not support color emojis fonts on
- # Windows and MacOS
- APP_SETTINGS['dev_use_message_label'] = False
-
def _load_app_overrides(self) -> None:
if not OVERRIDES_PATH.exists():
return
=====================================
gajim/common/styling.py
=====================================
@@ -19,7 +19,6 @@
from typing import Optional
import string
-import sys
import re
from dataclasses import dataclass
from dataclasses import field
@@ -31,7 +30,6 @@
from gajim.common.helpers import parse_uri as analyze_uri
from gajim.common.helpers import validate_jid
from gajim.common.text_helpers import jid_to_iri
-from gajim.gui.emoji_data import emoji_data
PRE = '`'
STRONG = '*'
@@ -53,9 +51,6 @@
URI_OR_JID_RX = re.compile(
fr'(?P<uri>(?<![\w+.-]){regex.IRI})|(?P<jid>{regex.XMPP.jid})')
-EMOJI_RX = emoji_data.get_regex()
-EMOJI_RX = re.compile(EMOJI_RX)
-
SD = 0
SD_POS = 1
MAX_QUOTE_LEVEL = 20
@@ -97,11 +92,6 @@ class MailAddress(BaseHyperlink):
name: str = field(default='mailadr', init=False)
-@dataclass
-class Emoji(StyleObject):
- name: str = field(default='emoji', init=False)
-
-
@dataclass
class Block(StyleObject):
@@ -117,7 +107,6 @@ class PlainBlock(Block):
name: str = field(default='plain', init=False)
spans: list[Span] = field(default_factory=list)
uris: list[BaseHyperlink] = field(default_factory=list)
- emojis: list[Emoji] = field(default_factory=list)
@classmethod
def from_quote_match(cls, match: Match[str]) -> PlainBlock:
@@ -208,10 +197,6 @@ def process(text: Union[str, bytes], level: int = 0) -> ParsingResult:
for line in block.text.splitlines(keepends=True):
block.spans += _parse_line(line, offset, offset_bytes)
block.uris += _parse_uris(line, offset, offset_bytes)
- if sys.platform == 'darwin':
- # block.emojis is used for replacing emojis with Gtk.Images
- # Necessary for MessageTextview (darwin) only
- block.emojis += _parse_emojis(line, offset)
offset += len(line)
offset_bytes += len(line.encode())
@@ -361,22 +346,6 @@ def make(start: int, end: int, is_jid: bool) -> Optional[BaseHyperlink]:
return uris
-def _parse_emojis(line: str, offset: int) -> list[Emoji]:
- emojis: list[Emoji] = []
-
- def make(match: Match[str]) -> Emoji:
- return _make_emoji(line,
- match.start(),
- match.end() - 1,
- offset)
-
- for match in EMOJI_RX.finditer(line):
- emoji = make(match)
- emojis.append(emoji)
-
- return emojis
-
-
def _handle_pre_span(line: str,
index: int,
offset: int,
@@ -464,18 +433,6 @@ def _make_hyperlink(line: str,
text=text)
-def _make_emoji(line: str,
- start: int,
- end: int,
- offset: int) -> Emoji:
- text = line[start:end + 1]
- start += offset
- end += offset + 1
- return Emoji(start=start,
- end=end,
- text=text)
-
-
def _is_span_empty(sd: str, index: int, stack: list[tuple[str, int]]) -> bool:
start_sd = stack[-1][SD]
if start_sd != sd:
=====================================
gajim/data/emoticons/noto/LICENSE deleted
=====================================
@@ -1,201 +0,0 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
=====================================
gajim/data/emoticons/noto/noto.png deleted
=====================================
Binary files a/gajim/data/emoticons/noto/noto.png and /dev/null differ
=====================================
gajim/data/emoticons/twemoji/LICENSE deleted
=====================================
@@ -1,393 +0,0 @@
-Attribution 4.0 International
-
-=======================================================================
-
-Creative Commons Corporation ("Creative Commons") is not a law firm and
-does not provide legal services or legal advice. Distribution of
-Creative Commons public licenses does not create a lawyer-client or
-other relationship. Creative Commons makes its licenses and related
-information available on an "as-is" basis. Creative Commons gives no
-warranties regarding its licenses, any material licensed under their
-terms and conditions, or any related information. Creative Commons
-disclaims all liability for damages resulting from their use to the
-fullest extent possible.
-
-Using Creative Commons Public Licenses
-
-Creative Commons public licenses provide a standard set of terms and
-conditions that creators and other rights holders may use to share
-original works of authorship and other material subject to copyright
-and certain other rights specified in the public license below. The
-following considerations are for informational purposes only, are not
-exhaustive, and do not form part of our licenses.
-
- Considerations for licensors: Our public licenses are
- intended for use by those authorized to give the public
- permission to use material in ways otherwise restricted by
- copyright and certain other rights. Our licenses are
- irrevocable. Licensors should read and understand the terms
- and conditions of the license they choose before applying it.
- Licensors should also secure all rights necessary before
- applying our licenses so that the public can reuse the
- material as expected. Licensors should clearly mark any
- material not subject to the license. This includes other CC-
- licensed material, or material used under an exception or
- limitation to copyright. More considerations for licensors:
- wiki.creativecommons.org/Considerations_for_licensors
-
- Considerations for the public: By using one of our public
- licenses, a licensor grants the public permission to use the
- licensed material under specified terms and conditions. If
- the licensor's permission is not necessary for any reason--for
- example, because of any applicable exception or limitation to
- copyright--then that use is not regulated by the license. Our
- licenses grant only permissions under copyright and certain
- other rights that a licensor has authority to grant. Use of
- the licensed material may still be restricted for other
- reasons, including because others have copyright or other
- rights in the material. A licensor may make special requests,
- such as asking that all changes be marked or described.
- Although not required by our licenses, you are encouraged to
- respect those requests where reasonable. More_considerations
- for the public:
- wiki.creativecommons.org/Considerations_for_licensees
-
-=======================================================================
-
-Creative Commons Attribution 4.0 International Public License
-
-By exercising the Licensed Rights (defined below), You accept and agree
-to be bound by the terms and conditions of this Creative Commons
-Attribution 4.0 International Public License ("Public License"). To the
-extent this Public License may be interpreted as a contract, You are
-granted the Licensed Rights in consideration of Your acceptance of
-these terms and conditions, and the Licensor grants You such rights in
-consideration of benefits the Licensor receives from making the
-Licensed Material available under these terms and conditions.
-
-
-Section 1 -- Definitions.
-
- a. Adapted Material means material subject to Copyright and Similar
- Rights that is derived from or based upon the Licensed Material
- and in which the Licensed Material is translated, altered,
- arranged, transformed, or otherwise modified in a manner requiring
- permission under the Copyright and Similar Rights held by the
- Licensor. For purposes of this Public License, where the Licensed
- Material is a musical work, performance, or sound recording,
- Adapted Material is always produced where the Licensed Material is
- synched in timed relation with a moving image.
-
- b. Adapter's License means the license You apply to Your Copyright
- and Similar Rights in Your contributions to Adapted Material in
- accordance with the terms and conditions of this Public License.
-
- c. Copyright and Similar Rights means copyright and/or similar rights
- closely related to copyright including, without limitation,
- performance, broadcast, sound recording, and Sui Generis Database
- Rights, without regard to how the rights are labeled or
- categorized. For purposes of this Public License, the rights
- specified in Section 2(b)(1)-(2) are not Copyright and Similar
- Rights.
-
- d. Effective Technological Measures means those measures that, in the
- absence of proper authority, may not be circumvented under laws
- fulfilling obligations under Article 11 of the WIPO Copyright
- Treaty adopted on December 20, 1996, and/or similar international
- agreements.
-
- e. Exceptions and Limitations means fair use, fair dealing, and/or
- any other exception or limitation to Copyright and Similar Rights
- that applies to Your use of the Licensed Material.
-
- f. Licensed Material means the artistic or literary work, database,
- or other material to which the Licensor applied this Public
- License.
-
- g. Licensed Rights means the rights granted to You subject to the
- terms and conditions of this Public License, which are limited to
- all Copyright and Similar Rights that apply to Your use of the
- Licensed Material and that the Licensor has authority to license.
-
- h. Licensor means the individual(s) or entity(ies) granting rights
- under this Public License.
-
- i. Share means to provide material to the public by any means or
- process that requires permission under the Licensed Rights, such
- as reproduction, public display, public performance, distribution,
- dissemination, communication, or importation, and to make material
- available to the public including in ways that members of the
- public may access the material from a place and at a time
- individually chosen by them.
-
- j. Sui Generis Database Rights means rights other than copyright
- resulting from Directive 96/9/EC of the European Parliament and of
- the Council of 11 March 1996 on the legal protection of databases,
- as amended and/or succeeded, as well as other essentially
- equivalent rights anywhere in the world.
-
- k. You means the individual or entity exercising the Licensed Rights
- under this Public License. Your has a corresponding meaning.
-
-
-Section 2 -- Scope.
-
- a. License grant.
-
- 1. Subject to the terms and conditions of this Public License,
- the Licensor hereby grants You a worldwide, royalty-free,
- non-sublicensable, non-exclusive, irrevocable license to
- exercise the Licensed Rights in the Licensed Material to:
-
- a. reproduce and Share the Licensed Material, in whole or
- in part; and
-
- b. produce, reproduce, and Share Adapted Material.
-
- 2. Exceptions and Limitations. For the avoidance of doubt, where
- Exceptions and Limitations apply to Your use, this Public
- License does not apply, and You do not need to comply with
- its terms and conditions.
-
- 3. Term. The term of this Public License is specified in Section
- 6(a).
-
- 4. Media and formats; technical modifications allowed. The
- Licensor authorizes You to exercise the Licensed Rights in
- all media and formats whether now known or hereafter created,
- and to make technical modifications necessary to do so. The
- Licensor waives and/or agrees not to assert any right or
- authority to forbid You from making technical modifications
- necessary to exercise the Licensed Rights, including
- technical modifications necessary to circumvent Effective
- Technological Measures. For purposes of this Public License,
- simply making modifications authorized by this Section 2(a)
- (4) never produces Adapted Material.
-
- 5. Downstream recipients.
-
- a. Offer from the Licensor -- Licensed Material. Every
- recipient of the Licensed Material automatically
- receives an offer from the Licensor to exercise the
- Licensed Rights under the terms and conditions of this
- Public License.
-
- b. No downstream restrictions. You may not offer or impose
- any additional or different terms or conditions on, or
- apply any Effective Technological Measures to, the
- Licensed Material if doing so restricts exercise of the
- Licensed Rights by any recipient of the Licensed
- Material.
-
- 6. No endorsement. Nothing in this Public License constitutes or
- may be construed as permission to assert or imply that You
- are, or that Your use of the Licensed Material is, connected
- with, or sponsored, endorsed, or granted official status by,
- the Licensor or others designated to receive attribution as
- provided in Section 3(a)(1)(A)(i).
-
- b. Other rights.
-
- 1. Moral rights, such as the right of integrity, are not
- licensed under this Public License, nor are publicity,
- privacy, and/or other similar personality rights; however, to
- the extent possible, the Licensor waives and/or agrees not to
- assert any such rights held by the Licensor to the limited
- extent necessary to allow You to exercise the Licensed
- Rights, but not otherwise.
-
- 2. Patent and trademark rights are not licensed under this
- Public License.
-
- 3. To the extent possible, the Licensor waives any right to
- collect royalties from You for the exercise of the Licensed
- Rights, whether directly or through a collecting society
- under any voluntary or waivable statutory or compulsory
- licensing scheme. In all other cases the Licensor expressly
- reserves any right to collect such royalties.
-
-
-Section 3 -- License Conditions.
-
-Your exercise of the Licensed Rights is expressly made subject to the
-following conditions.
-
- a. Attribution.
-
- 1. If You Share the Licensed Material (including in modified
- form), You must:
-
- a. retain the following if it is supplied by the Licensor
- with the Licensed Material:
-
- i. identification of the creator(s) of the Licensed
- Material and any others designated to receive
- attribution, in any reasonable manner requested by
- the Licensor (including by pseudonym if
- designated);
-
- ii. a copyright notice;
-
- iii. a notice that refers to this Public License;
-
- iv. a notice that refers to the disclaimer of
- warranties;
-
- v. a URI or hyperlink to the Licensed Material to the
- extent reasonably practicable;
-
- b. indicate if You modified the Licensed Material and
- retain an indication of any previous modifications; and
-
- c. indicate the Licensed Material is licensed under this
- Public License, and include the text of, or the URI or
- hyperlink to, this Public License.
-
- 2. You may satisfy the conditions in Section 3(a)(1) in any
- reasonable manner based on the medium, means, and context in
- which You Share the Licensed Material. For example, it may be
- reasonable to satisfy the conditions by providing a URI or
- hyperlink to a resource that includes the required
- information.
-
- 3. If requested by the Licensor, You must remove any of the
- information required by Section 3(a)(1)(A) to the extent
- reasonably practicable.
-
- 4. If You Share Adapted Material You produce, the Adapter's
- License You apply must not prevent recipients of the Adapted
- Material from complying with this Public License.
-
-
-Section 4 -- Sui Generis Database Rights.
-
-Where the Licensed Rights include Sui Generis Database Rights that
-apply to Your use of the Licensed Material:
-
- a. for the avoidance of doubt, Section 2(a)(1) grants You the right
- to extract, reuse, reproduce, and Share all or a substantial
- portion of the contents of the database;
-
- b. if You include all or a substantial portion of the database
- contents in a database in which You have Sui Generis Database
- Rights, then the database in which You have Sui Generis Database
- Rights (but not its individual contents) is Adapted Material; and
-
- c. You must comply with the conditions in Section 3(a) if You Share
- all or a substantial portion of the contents of the database.
-
-For the avoidance of doubt, this Section 4 supplements and does not
-replace Your obligations under this Public License where the Licensed
-Rights include other Copyright and Similar Rights.
-
-
-Section 5 -- Disclaimer of Warranties and Limitation of Liability.
-
- a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
- EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
- AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
- ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
- IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
- WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
- PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
- ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
- KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
- ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
-
- b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
- TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
- NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
- INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
- COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
- USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
- ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
- DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
- IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
-
- c. The disclaimer of warranties and limitation of liability provided
- above shall be interpreted in a manner that, to the extent
- possible, most closely approximates an absolute disclaimer and
- waiver of all liability.
-
-
-Section 6 -- Term and Termination.
-
- a. This Public License applies for the term of the Copyright and
- Similar Rights licensed here. However, if You fail to comply with
- this Public License, then Your rights under this Public License
- terminate automatically.
-
- b. Where Your right to use the Licensed Material has terminated under
- Section 6(a), it reinstates:
-
- 1. automatically as of the date the violation is cured, provided
- it is cured within 30 days of Your discovery of the
- violation; or
-
- 2. upon express reinstatement by the Licensor.
-
- For the avoidance of doubt, this Section 6(b) does not affect any
- right the Licensor may have to seek remedies for Your violations
- of this Public License.
-
- c. For the avoidance of doubt, the Licensor may also offer the
- Licensed Material under separate terms or conditions or stop
- distributing the Licensed Material at any time; however, doing so
- will not terminate this Public License.
-
- d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
- License.
-
-
-Section 7 -- Other Terms and Conditions.
-
- a. The Licensor shall not be bound by any additional or different
- terms or conditions communicated by You unless expressly agreed.
-
- b. Any arrangements, understandings, or agreements regarding the
- Licensed Material not stated herein are separate from and
- independent of the terms and conditions of this Public License.
-
-
-Section 8 -- Interpretation.
-
- a. For the avoidance of doubt, this Public License does not, and
- shall not be interpreted to, reduce, limit, restrict, or impose
- conditions on any use of the Licensed Material that could lawfully
- be made without permission under this Public License.
-
- b. To the extent possible, if any provision of this Public License is
- deemed unenforceable, it shall be automatically reformed to the
- minimum extent necessary to make it enforceable. If the provision
- cannot be reformed, it shall be severed from this Public License
- without affecting the enforceability of the remaining terms and
- conditions.
-
- c. No term or condition of this Public License will be waived and no
- failure to comply consented to unless expressly agreed to by the
- Licensor.
-
- d. Nothing in this Public License constitutes or may be interpreted
- as a limitation upon, or waiver of, any privileges and immunities
- that apply to the Licensor or You, including from the legal
- processes of any jurisdiction or authority.
-
-
-=======================================================================
-
-Creative Commons is not a party to its public licenses.
-Notwithstanding, Creative Commons may elect to apply one of its public
-licenses to material it publishes and in those instances will be
-considered the "Licensor." Except for the limited purpose of indicating
-that material is shared under a Creative Commons public license or as
-otherwise permitted by the Creative Commons policies published at
-creativecommons.org/policies, Creative Commons does not authorize the
-use of the trademark "Creative Commons" or any other trademark or logo
-of Creative Commons without its prior written consent including,
-without limitation, in connection with any unauthorized modifications
-to any of its public licenses or any other arrangements,
-understandings, or agreements concerning use of licensed material. For
-the avoidance of doubt, this paragraph does not form part of the public
-licenses.
-
-Creative Commons may be contacted at creativecommons.org.
\ No newline at end of file
=====================================
gajim/data/emoticons/twemoji/twemoji.png deleted
=====================================
Binary files a/gajim/data/emoticons/twemoji/twemoji.png and /dev/null differ
=====================================
gajim/data/gui/message_actions_box.ui
=====================================
@@ -143,6 +143,7 @@
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="tooltip-text" translatable="yes">Show a list of emojis</property>
+ <property name="action-name">win.show-emoji-chooser</property>
<property name="relief">none</property>
<child>
<object class="GtkImage">
=====================================
gajim/data/gui/preferences.ui
=====================================
@@ -688,57 +688,7 @@
</packing>
</child>
<child>
- <!-- n-columns=3 n-rows=3 -->
- <object class="GtkGrid" id="emoji">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
- <property name="orientation">vertical</property>
- <property name="row-spacing">12</property>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
- <property name="label" translatable="yes">Emoji</property>
- <property name="xalign">0</property>
- <style>
- <class name="bold"/>
- </style>
- </object>
- <packing>
- <property name="left-attach">0</property>
- <property name="top-attach">0</property>
- </packing>
- </child>
- <child>
- <placeholder/>
- </child>
- <child>
- <placeholder/>
- </child>
- <child>
- <placeholder/>
- </child>
- <child>
- <placeholder/>
- </child>
- <child>
- <placeholder/>
- </child>
- <child>
- <placeholder/>
- </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>
+ <placeholder/>
</child>
</object>
</child>
=====================================
gajim/data/style/gajim.css
=====================================
@@ -748,18 +748,6 @@ #NotificationPopupTitle {
font-weight: bold;
}
-/* Emoticon Popover (ModifierChooser) */
-popover#EmoticonPopover button { background: none; border: none; box-shadow:none; padding: 0px;}
-popover#EmoticonPopover button > label { font-size: 2.0rem; }
-popover#EmoticonPopover flowboxchild > label { font-size: 2.0rem; }
-popover#EmoticonPopover flowboxchild { padding-top: 5px; padding-bottom: 5px; }
-popover#EmoticonPopover scrolledwindow { border: none; }
-popover#EmoticonPopover { padding: 5px; background-color: @theme_unfocused_base_color; }
-
-.emoji-chooser-heading { font-size: 1.15rem; font-weight: bold; padding: 5px;}
-.emoji-chooser-flowbox { padding-left: 5px; padding-right: 11px; }
-.emoji-modifier-chooser-flowbox { padding-left: 5px; }
-
/* Settings widgets ======================================================== */
/* SettingsBox */
=====================================
gajim/gtk/application.py
=====================================
@@ -244,12 +244,6 @@ def _startup(self) -> None:
idle.Monitor.set_interval(app.settings.get('autoawaytime') * 60,
app.settings.get('autoxatime') * 60)
- if sys.platform == 'darwin':
- # TODO: Remove if colored emoji rendering works well on
- # Windows and MacOS
- from gajim.gui.emoji_chooser import emoji_chooser
- emoji_chooser.load()
-
from gajim.gui_interface import Interface
self.interface = Interface()
=====================================
gajim/gtk/chat_stack.py
=====================================
@@ -712,7 +712,6 @@ def _on_function_message(self,
self._chat_control.add_info_message(message)
def _on_send_message(self) -> None:
- self._message_action_box.msg_textview.replace_emojis()
message = self._message_action_box.msg_textview.get_text()
if message.startswith('//'):
# Escape sequence for chat commands
=====================================
gajim/gtk/conversation/plain_widget.py
=====================================
@@ -14,32 +14,19 @@
from __future__ import annotations
-from typing import Any
-
import logging
-import sys
from gi.repository import Gtk
from gi.repository import Pango
from gi.repository import Gdk
-from gi.repository import GdkPixbuf
from gi.repository import GLib
-from gi.repository import GObject
-from gajim.common import app
-from gajim.common.const import StyleAttr
from gajim.common.helpers import open_uri
-from gajim.common.helpers import parse_uri
-from gajim.common.structs import URI
from gajim.common.styling import BaseHyperlink
from gajim.common.styling import PlainBlock
from gajim.common.styling import process_uris
from ..menus import get_conv_action_context_menu
-from ..menus import get_conv_uri_context_menu
-from ..emoji_data import emoji_pixbufs
-from ..emoji_data import get_emoji_pixbuf
-from ..util import get_cursor
from ..util import make_pango_attributes
log = logging.getLogger('gajim.gui.conversaion.plain_widget')
@@ -55,22 +42,11 @@ def __init__(self, account: str, selectable: bool) -> None:
self._account = account
- # We use a Gtk.Textview on MacOS, since there is no support
- # for rendering color fonts (Emojis) on MacOS yet, see:
- # https://gitlab.freedesktop.org/cairo/cairo/-/merge_requests/289
- # https://gitlab.freedesktop.org/cairo/cairo/-/merge_requests/9
- if sys.platform == 'darwin':
- if app.settings.get('dev_use_message_label'):
- self._text_widget = MessageLabel(self._account, selectable)
- else:
- self._text_widget = MessageTextview(self._account)
- else:
- self._text_widget = MessageLabel(self._account, selectable)
+ self._text_widget = MessageLabel(self._account, selectable)
self.add(self._text_widget)
def set_selectable(self, selectable: bool) -> None:
- if isinstance(self._text_widget, MessageLabel):
- self._text_widget.set_selectable(selectable)
+ self._text_widget.set_selectable(selectable)
def add_content(self, block: PlainBlock) -> None:
self._text_widget.print_text_with_styling(block)
@@ -152,295 +128,3 @@ def _on_focus_out(widget: MessageLabel,
_event: Gdk.EventFocus
) -> None:
widget.get_style_context().add_class('transparent-selection')
-
-
-class MessageTextview(Gtk.TextView):
- def __init__(self, account: str) -> None:
- Gtk.TextView.__init__(self)
- self.set_hexpand(True)
- self.set_margin_start(0)
- self.set_margin_end(0)
- self.set_border_width(0)
- self.set_left_margin(0)
- self.set_right_margin(0)
- self.set_has_tooltip(True)
- self.set_editable(False)
- self.set_cursor_visible(False)
- self.set_wrap_mode(Gtk.WrapMode.WORD_CHAR)
-
- self._handlers: dict[int, MessageTextview] = {}
-
- id_ = self.connect('query-tooltip', self._query_tooltip)
- self._handlers[id_] = self
- id_ = self.connect('button-press-event', self._on_button_press)
- self._handlers[id_] = self
- id_ = self.connect('populate-popup', self._on_populate_popup)
- self._handlers[id_] = self
-
- self._account = account
-
- # Used for changing the mouse pointer when hovering clickable URIs
- self._cursor_changed: bool = False
-
- # Keeps text selections for quoting and search actions
- self._selected_text: str = ''
-
- self.get_style_context().add_class('gajim-conversation-text')
-
- # Create Tags
- self._create_url_tags()
- self.get_buffer().create_tag('strong', weight=Pango.Weight.BOLD)
- self.get_buffer().create_tag('emphasis', style=Pango.Style.ITALIC)
- self.get_buffer().create_tag('strike', strikethrough=True)
- self.get_buffer().create_tag('pre', family='monospace')
-
- self.connect('destroy', self._on_destroy)
-
- def _on_destroy(self, *args: Any) -> None:
- for id_, widget in self._handlers.items():
- if widget.handler_is_connected(id_):
- widget.disconnect(id_)
- self._handlers.clear()
-
- def _create_url_tags(self) -> None:
- color = app.css_config.get_value('.gajim-url', StyleAttr.COLOR)
- for name in URI_TAGS:
- tag = self.get_buffer().create_tag(name,
- foreground=color,
- underline=Pango.Underline.SINGLE)
- tag.connect('event', self._on_uri_clicked, tag)
-
- def clear(self) -> None:
- buffer_ = self.get_buffer()
- start, end = buffer_.get_bounds()
- buffer_.delete(start, end)
-
- def get_text(self) -> str:
- buffer_ = self.get_buffer()
- start, end = buffer_.get_bounds()
- return buffer_.get_text(start, end, False)
-
- def print_text_with_styling(self, block: PlainBlock) -> None:
- buffer_ = self.get_buffer()
- buffer_.insert(buffer_.get_start_iter(), block.text.strip())
-
- if len(self.get_text()) > 10000:
- # Limit message styling processing
- return
-
- for span in block.spans:
- start_iter = buffer_.get_iter_at_offset(span.start)
- end_iter = buffer_.get_iter_at_offset(span.end)
- buffer_.apply_tag_by_name(span.name, start_iter, end_iter)
-
- for uri in block.uris:
- start_iter = buffer_.get_iter_at_offset(uri.start)
- end_iter = buffer_.get_iter_at_offset(uri.end)
- buffer_.apply_tag_by_name(uri.name, start_iter, end_iter)
-
- for emoji in block.emojis:
- start_iter = buffer_.get_iter_at_offset(emoji.start)
- end_iter = buffer_.get_iter_at_offset(emoji.end)
- if emoji_pixbufs.complete:
- # Only search for pixbuf if loading is completed
- pixbuf = get_emoji_pixbuf(emoji.text)
- if pixbuf is None:
- buffer_.insert(end_iter, emoji.text)
- else:
- pixbuf = pixbuf.copy()
- anchor = buffer_.create_child_anchor(end_iter)
- anchor.plaintext = emoji.text # type: ignore
- img = Gtk.Image.new_from_pixbuf(pixbuf)
- img.show()
- self.add_child_at_anchor(img, anchor)
- buffer_.delete(
- buffer_.get_iter_at_offset(emoji.start),
- buffer_.get_iter_at_offset(emoji.end))
- else:
- # Set marks and save them so we can replace emojis
- # once loading is complete
- start_mark = buffer_.create_mark(None, end_iter, True)
- buffer_.insert(end_iter, emoji.text)
- end_mark = buffer_.create_mark(None, end_iter, True)
- emoji_pixbufs.append_marks(
- self, start_mark, end_mark, emoji.text)
-
- def replace_emoji(self,
- start_mark: Gtk.TextMark,
- end_mark: Gtk.TextMark,
- pixbuf: GdkPixbuf.Pixbuf,
- codepoint: str
- ) -> None:
- buffer_ = self.get_buffer()
- start_iter = buffer_.get_iter_at_mark(start_mark)
- end_iter = buffer_.get_iter_at_mark(end_mark)
- buffer_.delete(start_iter, end_iter)
-
- anchor = buffer_.create_child_anchor(start_iter)
- anchor.plaintext = codepoint # type: ignore
- img = Gtk.Image.new_from_pixbuf(pixbuf)
- img.show()
- self.add_child_at_anchor(img, anchor)
- buffer_.delete_mark(start_mark)
- buffer_.delete_mark(end_mark)
-
- def add_action_phrase(self, text: str, nickname: str) -> None:
- text = text.replace('/me', f'* {nickname}', 1)
-
- buffer_ = self.get_buffer()
- buffer_.insert(buffer_.get_start_iter(), text.strip())
-
- uris = process_uris(text)
- for uri in uris:
- start_iter = buffer_.get_iter_at_offset(uri.start)
- end_iter = buffer_.get_iter_at_offset(uri.end)
- buffer_.apply_tag_by_name(uri.name, start_iter, end_iter)
-
- start_iter = buffer_.get_start_iter()
- end_iter = buffer_.get_end_iter()
- buffer_.apply_tag_by_name('emphasis', start_iter, end_iter)
-
- def _query_tooltip(self,
- widget: Gtk.TextView,
- x_pos: int,
- y_pos: int,
- _keyboard_mode: bool,
- _tooltip: Gtk.Tooltip
- ) -> bool:
-
- window = widget.get_window(Gtk.TextWindowType.TEXT)
- assert window is not None
- x_pos, y_pos = self.window_to_buffer_coords(
- Gtk.TextWindowType.TEXT, x_pos, y_pos)
-
- iter_ = self.get_iter_at_position(x_pos, y_pos)[1]
- for tag in iter_.get_tags():
- tag_name = tag.get_property('name')
- if tag_name in URI_TAGS:
- window.set_cursor(get_cursor('pointer'))
- self._cursor_changed = True
- return False
-
- if self._cursor_changed:
- window.set_cursor(get_cursor('text'))
- self._cursor_changed = False
- return False
-
- def _on_button_press(self, _widget: Any, event: Gdk.EventButton) -> bool:
- '''
- We don’t open the standard context menu when receiving
- a click on tagged text.
- If it’s untagged text, check if something is selected
- '''
- self._selected_text = ''
-
- if event.button != 3:
- # If it’s not a right click
- return False
-
- x_pos, y_pos = self.window_to_buffer_coords(
- Gtk.TextWindowType.TEXT,
- int(event.x),
- int(event.y))
- _, iter_ = self.get_iter_at_location(x_pos, y_pos)
- tags = iter_.get_tags()
-
- if tags:
- # A tagged text fragment has been clicked
- for tag in tags:
- if tag.get_property('name') in URI_TAGS:
- # Block regular context menu
- return True
-
- # Check if there is a selection and make it available for
- # _on_populate_popup
- buffer_ = self.get_buffer()
- return_val = buffer_.get_selection_bounds()
- if return_val:
- # Something has been selected, get the text
- start_sel, finish_sel = return_val[0], return_val[1]
- self._selected_text = buffer_.get_text(
- start_sel, finish_sel, True)
- elif iter_.get_char() and ord(iter_.get_char()) > 31:
- # Clicked on a word, take whole word for selection
- start_sel = iter_.copy()
- if not start_sel.starts_word():
- start_sel.backward_word_start()
- finish_sel = iter_.copy()
- if not finish_sel.ends_word():
- finish_sel.forward_word_end()
- self._selected_text = buffer_.get_text(
- start_sel, finish_sel, True)
- return False
-
- def _on_populate_popup(self,
- _textview: Gtk.TextView,
- menu: Gtk.Menu
- ) -> None:
- '''
- Overrides the default context menu.
- If text is selected, a submenu with actions on the selection is added.
- (see _on_button_press)
- '''
- if not self._selected_text:
- menu.show_all()
- return
-
- action_menu_item = get_conv_action_context_menu(
- self._account, self._selected_text)
- menu.prepend(action_menu_item)
- menu.show_all()
-
- def _on_uri_clicked(self,
- texttag: Gtk.TextTag,
- _widget: Any,
- event: Gdk.Event,
- iter_: Gtk.TextIter,
- _kind: Gtk.TextTag
- ) -> int:
- if event.type != Gdk.EventType.BUTTON_PRESS:
- return Gdk.EVENT_PROPAGATE
-
- begin_iter = iter_.copy()
- # we get the beginning of the tag
- while not begin_iter.starts_tag(texttag):
- begin_iter.backward_char()
- end_iter = iter_.copy()
- # we get the end of the tag
- while not end_iter.ends_tag(texttag):
- end_iter.forward_char()
-
- # Detect XHTML-IM link
- word = getattr(texttag, 'href', None)
- if not word:
- word = self.get_buffer().get_text(begin_iter, end_iter, True)
-
- uri = parse_uri(word)
- if event.button.button == 3: # right click
- self._show_uri_context_menu(uri)
- return Gdk.EVENT_STOP
-
- # TODO:
- # self.plugin_modified = False
- # app.plugin_manager.extension_point(
- # 'hyperlink_handler', uri, self, self.get_toplevel())
- # if self.plugin_modified:
- # return Gdk.EVENT_STOP
-
- open_uri(uri, account=self._account)
- return Gdk.EVENT_STOP
-
- def _show_uri_context_menu(self, uri: URI) -> None:
- menu = get_conv_uri_context_menu(self._account, uri)
- if menu is None:
- log.warning('No handler for URI type: %s', uri)
- return
-
- def _destroy(menu: Gtk.Menu, _pspec: GObject.ParamSpec) -> None:
- visible = menu.get_property('visible')
- if not visible:
- GLib.idle_add(menu.destroy)
-
- menu.attach_to_widget(self, None)
- menu.connect('notify::visible', _destroy)
- menu.popup_at_pointer()
=====================================
gajim/gtk/emoji_chooser.py deleted
=====================================
@@ -1,412 +0,0 @@
-# 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/>.
-
-import logging
-import weakref
-from collections import OrderedDict
-
-from gi.repository import Gtk
-from gi.repository import GLib
-from gi.repository import GdkPixbuf
-from gi.repository import Pango
-
-from gajim.common import app
-from gajim.common import helpers
-from gajim.common import configpaths
-
-from .builder import get_builder
-from .emoji_data import emoji_data
-from .emoji_data import emoji_pixbufs
-from .emoji_data import Emoji
-
-MODIFIER_MAX_CHILDREN_PER_LINE = 6
-MAX_CHILDREN_PER_LINE = 10
-
-log = logging.getLogger('gajim.emoji')
-
-
-class Section(Gtk.Box):
- def __init__(self, name, search_entry, press_cb, chooser):
- Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL)
- self._chooser = chooser
- self._press_cb = press_cb
- self.pixbuf_generator = None
- self.heading = Gtk.Label(label=name)
- self.heading.set_halign(Gtk.Align.START)
- self.heading.get_style_context().add_class('emoji-chooser-heading')
- self.add(self.heading)
-
- self.flowbox = Gtk.FlowBox()
- self.flowbox.get_style_context().add_class('emoji-chooser-flowbox')
- self.flowbox.set_max_children_per_line(MAX_CHILDREN_PER_LINE)
- self.flowbox.set_filter_func(self._filter_func, search_entry)
- self.flowbox.connect('child-activated', press_cb)
-
- self.add(self.flowbox)
- self.show_all()
-
- def _filter_func(self, child, search_entry):
- name = search_entry.get_text()
- if not name:
- self.show()
- return True
-
- if name in child.desc:
- self.show()
- return True
- return False
-
- def add_emoji(self, codepoint, attrs):
- # Return always True, this method is used for the emoji factory
- # called by GLib.idle_add()
- pixbuf = self._get_next_pixbuf()
-
- variations = attrs.get('variations', None)
- if variations is None:
- if pixbuf is None:
- return True
- self.flowbox.add(EmojiChild(codepoint, pixbuf, attrs['desc']))
- if pixbuf != 'font':
- # We save the pixbuf for fast access if we need to
- # replace a codepoint in the textview
- emoji_pixbufs[codepoint] = pixbuf
- else:
- if pixbuf is not None:
- chooser = ModifierChooser()
-
- # Iterate over the variations and add the codepoints
- for codepoint_ in variations.keys():
- pixbuf_ = self._get_next_pixbuf()
- if pixbuf_ is None:
- continue
-
- if pixbuf_ == 'font':
- if not self._chooser.font_supports_codepoint(
- codepoint_):
- continue
- else:
- emoji_pixbufs[codepoint_] = pixbuf_
-
- # Only codepoints are added which the
- # font or theme supports
- chooser.add_emoji(codepoint_, pixbuf_)
-
- # Check if we successfully added codepoints with modifiers
- if chooser.has_child:
- # If we have children then add a button
- # and set the popover
- child = EmojiModifierChild(
- codepoint, pixbuf, attrs['desc'])
- child.button.set_popover(chooser)
- chooser.flowbox.connect(
- 'child-activated', self._press_cb)
- else:
- # If no children were added, destroy the chooser
- # and add a EmojiChild instead of a EmojiModifierChild
- chooser.destroy()
- child = EmojiChild(codepoint, pixbuf, attrs['desc'])
-
- if pixbuf != 'font':
- emoji_pixbufs[codepoint] = pixbuf
-
- self.flowbox.add(child)
- else:
- # We don’t have a image for the base codepoint
- # so skip all modifiers of it
- for _entry in variations:
- pixbuf = self._get_next_pixbuf()
- return True
-
- def clear_emojis(self):
- def _remove_emoji(emoji):
- self.flowbox.remove(emoji)
- emoji.destroy()
- self.flowbox.foreach(_remove_emoji)
-
- def _get_next_pixbuf(self):
- if self.pixbuf_generator is None:
- return 'font'
- return next(self.pixbuf_generator, False)
-
-
-class EmojiChild(Gtk.FlowBoxChild):
- def __init__(self, codepoint, pixbuf, desc):
- Gtk.FlowBoxChild.__init__(self)
- self.desc = desc
- self.codepoint = codepoint
- self.pixbuf = pixbuf
- if pixbuf == 'font':
- self.add(Gtk.Label(label=codepoint))
- else:
- self.add(Gtk.Image.new_from_pixbuf(pixbuf))
- self.set_tooltip_text(desc)
- self.show_all()
-
- def get_emoji(self):
- if self.pixbuf != 'font':
- pixbuf = self.get_child().get_pixbuf()
- pixbuf = pixbuf.scale_simple(Emoji.INPUT_SIZE,
- Emoji.INPUT_SIZE,
- GdkPixbuf.InterpType.HYPER)
- return self.codepoint, pixbuf
- return self.codepoint, None
-
-
-class EmojiModifierChild(Gtk.FlowBoxChild):
- def __init__(self, codepoint, pixbuf, desc):
- Gtk.FlowBoxChild.__init__(self)
- self.desc = desc
- self.codepoint = codepoint
- self.pixbuf = pixbuf
-
- self.button = Gtk.MenuButton()
- self.button.set_relief(Gtk.ReliefStyle.NONE)
- self.button.connect('button-press-event', self._button_press)
-
- if pixbuf == 'font':
- self.button.remove(self.button.get_child())
- label = Gtk.Label(label=codepoint)
- self.button.add(label)
- else:
- self.button.get_child().set_from_pixbuf(pixbuf)
-
- self.set_tooltip_text(desc)
- self.add(self.button)
- self.show_all()
-
- def _button_press(self, button, event):
- if event.button == 3:
- button.get_popover().show()
- button.get_popover().get_child().unselect_all()
- return True
- if event.button == 1:
- self.get_parent().emit('child-activated', self)
- return True
- return False
-
- def get_emoji(self):
- if self.pixbuf != 'font':
- pixbuf = self.button.get_child().get_pixbuf()
- pixbuf = pixbuf.scale_simple(Emoji.INPUT_SIZE,
- Emoji.INPUT_SIZE,
- GdkPixbuf.InterpType.HYPER)
- return self.codepoint, pixbuf
- return self.codepoint, None
-
-
-class ModifierChooser(Gtk.Popover):
- def __init__(self):
- Gtk.Popover.__init__(self)
- self.set_name('EmoticonPopover')
- self._has_child = False
-
- self.flowbox = Gtk.FlowBox()
- self.flowbox.get_style_context().add_class(
- 'emoji-modifier-chooser-flowbox')
- self.flowbox.set_size_request(200, -1)
- self.flowbox.set_max_children_per_line(MODIFIER_MAX_CHILDREN_PER_LINE)
- self.flowbox.show()
- self.add(self.flowbox)
-
- @property
- def has_child(self):
- return self._has_child
-
- def add_emoji(self, codepoint, pixbuf):
- self.flowbox.add(EmojiChild(codepoint, pixbuf, None))
- self._has_child = True
-
-
-class EmojiChooser(Gtk.Popover):
-
- _section_names = [
- 'Smileys & People',
- 'Animals & Nature',
- 'Food & Drink',
- 'Travel & Places',
- 'Activities',
- 'Objects',
- 'Symbols',
- 'Flags'
- ]
-
- def __init__(self):
- super().__init__()
- self.set_name('EmoticonPopover')
- self._text_widget = None
- self._load_source_id = None
- self._pango_layout = Pango.Layout(self.get_pango_context())
-
- self._builder = get_builder('emoji_chooser.ui')
- self._search = self._builder.get_object('search')
- self._stack = self._builder.get_object('stack')
-
- self._sections = OrderedDict()
- for name in self._section_names:
- self._sections[name] = Section(
- name, self._search, self._on_emoticon_press, self)
-
- section_box = self._builder.get_object('section_box')
- for section in self._sections.values():
- section_box.add(section)
-
- self.add(self._builder.get_object('box'))
-
- self.connect('key-press-event', self._key_press)
- self._builder.connect_signals(self)
-
- @property
- def text_widget(self):
- return self._text_widget
-
- @text_widget.setter
- def text_widget(self, value):
- # Hold only a weak reference so if we can destroy
- # the ChatControl
- self._text_widget = weakref.ref(value)
-
- def _key_press(self, _widget, event):
- return self._search.handle_event(event)
-
- def _search_changed(self, _entry):
- for section in self._sections.values():
- section.hide()
- section.flowbox.invalidate_filter()
- self._switch_stack()
-
- def _clear_sections(self):
- for section in self._sections.values():
- section.clear_emojis()
-
- def _switch_stack(self):
- for section in self._sections.values():
- if section.is_visible():
- self._stack.set_visible_child_name('list')
- return
- self._stack.set_visible_child_name('not-found')
-
- @staticmethod
- def _get_current_theme():
- theme = app.settings.get('emoticons_theme')
- themes = helpers.get_available_emoticon_themes()
- if theme not in themes:
- app.settings.set('emoticons_theme', 'noto')
- theme = 'noto'
- return theme
-
- @staticmethod
- def _get_emoji_theme_path(theme):
- if theme == 'font':
- return 'font'
-
- base_path = configpaths.get('EMOTICONS')
- emoticons_data_path = base_path / theme / f'{theme}.png'
- if emoticons_data_path.exists():
- return emoticons_data_path
-
- emoticons_user_path = configpaths.get('MY_EMOTS') / f'{theme}.png'
- if emoticons_user_path.exists():
- return emoticons_user_path
-
- log.warning('Could not find emoji theme: %s', theme)
- return None
-
- def load(self):
- theme = self._get_current_theme()
- path = self._get_emoji_theme_path(theme)
- if not theme or path is None:
- self._clear_sections()
- emoji_pixbufs.clear()
- return
-
- # Attach pixbuf generator
- pixbuf_generator = None
- if theme != 'font':
- pixbuf_generator = self._get_next_pixbuf(path)
- for section in self._sections.values():
- section.pixbuf_generator = pixbuf_generator
-
- if self._load_source_id is not None:
- GLib.source_remove(self._load_source_id)
-
- # When we change emoji theme
- self._clear_sections()
- emoji_pixbufs.clear()
-
- factory = self._emoji_factory(theme == 'font')
- self._load_source_id = GLib.idle_add(lambda: next(factory, False),
- priority=GLib.PRIORITY_LOW)
-
- def _emoji_factory(self, font):
- for codepoint, attrs in emoji_data.items():
- if not attrs['fully-qualified']:
- # We don’t add these to the UI
- continue
-
- if font and not self.font_supports_codepoint(codepoint):
- continue
-
- section = self._sections[attrs['group']]
- yield section.add_emoji(codepoint, attrs)
- self._load_source_id = None
- emoji_pixbufs.complete = True
-
- def font_supports_codepoint(self, codepoint):
- self._pango_layout.set_text(codepoint, -1)
- if self._pango_layout.get_unknown_glyphs_count():
- return False
- if len(codepoint) > 1:
- # The font supports each of the codepoints
- # Check if the rendered glyph is more than one char
- if self._pango_layout.get_size()[0] > 19000:
- return False
- return True
-
- @staticmethod
- def _get_next_pixbuf(path):
- src_x = src_y = cur_column = 0
- atlas = GdkPixbuf.Pixbuf.new_from_file(str(path))
-
- while True:
- src_x = cur_column * Emoji.PARSE_WIDTH
- subpixbuf = atlas.new_subpixbuf(src_x, src_y,
- Emoji.PARSE_WIDTH,
- Emoji.PARSE_HEIGHT)
-
- if list(subpixbuf.get_pixels())[0:4] == [0, 0, 0, 255]:
- # top left corner is a black pixel means image is missing
- subpixbuf = None
-
- if cur_column == Emoji.PARSE_COLUMNS - 1:
- src_y += Emoji.PARSE_WIDTH
- cur_column = 0
- else:
- cur_column += 1
-
- yield subpixbuf
-
- def _on_emoticon_press(self, flowbox, child):
- GLib.timeout_add(100, flowbox.unselect_child, child)
- codepoint, pixbuf = child.get_emoji()
- self._text_widget().insert_emoji(codepoint, pixbuf)
- self._text_widget().grab_focus()
-
- def do_destroy(self):
- # Remove the references we hold to other objects
- self._text_widget = None
- # Never destroy, creating a new EmoticonPopover is expensive
- return True
-
-
-emoji_chooser = EmojiChooser()
=====================================
gajim/gtk/emoji_data.py deleted
=====================================
The diff for this file was not included because it is too large.
=====================================
gajim/gtk/emoji_data.pyi deleted
=====================================
@@ -1,44 +0,0 @@
-# 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 enum import IntEnum
-from collections import OrderedDict
-
-from gi.repository import GdkPixbuf
-from gi.repository import Gtk
-
-class Emoji(IntEnum):
- ...
-
-def is_emoji(codepoints: str) -> bool: ...
-def get_emoji_pixbuf(codepoints: str) -> GdkPixbuf.Pixbuf: ...
-
-class EmojiData(OrderedDict):
- def get_regex(self) -> str: ...
-
-class EmojiPixbufs(dict):
- @property
- def complete(self) -> bool: ...
- @complete.setter
- def complete(self, value: bool) -> None: ...
- def clear(self) -> None: ...
- def append_marks(self,
- textview: Gtk.TextView,
- start: Gtk.TextMark,
- end: Gtk.TextMark,
- codepoint: str) -> None: ...
-
-emoji_pixbufs: EmojiPixbufs
-emoji_ascii_data: dict[str, str]
-emoji_data: EmojiData
=====================================
gajim/gtk/message_actions_box.py
=====================================
@@ -18,7 +18,6 @@
from typing import Optional
import os
-import sys
import tempfile
import logging
import uuid
@@ -45,7 +44,6 @@
from .builder import get_builder
from .dialogs import ErrorDialog
-from .emoji_chooser import emoji_chooser
from .menus import get_encryption_menu
from .menus import get_format_menu
from .menus import get_groupchat_menu
@@ -98,8 +96,6 @@ def __init__(self) -> None:
self._ui.quick_invite_button.set_action_name('win.muc-invite')
- self._init_emoticon_popover()
-
self.show_all()
self._ui.connect_signals(self)
@@ -157,7 +153,7 @@ def _on_action(self,
self._on_format(action_name)
elif action_name == 'show-emoji-chooser':
- self._on_show_emoji_chooser()
+ self.msg_textview.emit('insert-emoji')
elif action_name == 'quote':
assert param
@@ -389,31 +385,6 @@ def _set_settings_menu(self, contact: ChatContactT) -> None:
menu = get_singlechat_menu(contact)
self._ui.settings_menu.set_menu_model(menu)
- def _init_emoticon_popover(self) -> None:
- if not app.settings.get('emoticons_theme'):
- return
-
- if sys.platform == 'darwin':
- emoji_chooser.text_widget = self.msg_textview
- self._ui.emoticons_button.set_popover(emoji_chooser)
- return
-
- self._ui.emoticons_button.set_sensitive(True)
- self._ui.emoticons_button.connect('clicked',
- self._on_emoticon_button_clicked)
-
- def toggle_emoticons(self) -> None:
- if app.settings.get('emoticons_theme'):
- self._ui.emoticons_button.set_no_show_all(False)
- self._ui.emoticons_button.show()
- else:
- self._ui.emoticons_button.set_no_show_all(True)
- self._ui.emoticons_button.hide()
-
- def _on_emoticon_button_clicked(self, _widget: Gtk.Button) -> None:
- self.msg_textview.emit('insert-emoji')
- self._ui.emoticons_button.set_active(False)
-
def _on_format(self, name: str) -> None:
name = name.removeprefix('input-')
self.msg_textview.apply_formatting(name)
@@ -421,14 +392,6 @@ def _on_format(self, name: str) -> None:
def _on_clear(self) -> None:
self.msg_textview.clear()
- def _on_show_emoji_chooser(self) -> None:
- if sys.platform == 'darwin':
- popover = self._ui.emoticons_button.get_popover()
- assert popover
- popover.show()
- else:
- self.msg_textview.emit('insert-emoji')
-
def _on_send_file_enabled_changed(self,
action: Gio.SimpleAction,
_param: GObject.ParamSpec) -> None:
=====================================
gajim/gtk/message_input.py
=====================================
@@ -25,11 +25,9 @@
from collections import defaultdict
import logging
-import sys
from gi.repository import Gtk
from gi.repository import Gdk
-from gi.repository import GdkPixbuf
from gi.repository import GLib
from gi.repository import Pango
@@ -420,60 +418,6 @@ def apply_formatting(self, formatting: str) -> None:
buf.get_iter_at_offset(start_offset),
buf.get_iter_at_offset(end_offset + 2))
- def replace_emojis(self) -> None:
- if sys.platform != 'darwin':
- # TODO: Remove if colored emoji rendering works well on MacOS
- return
-
- theme = app.settings.get('emoticons_theme')
- if not theme or theme == 'font':
- return
-
- def _replace(anchor: Gtk.TextChildAnchor) -> None:
- if anchor is None:
- return
- image = anchor.get_widgets()[0]
- if hasattr(image, 'codepoint'):
- # found emoji
- codepoint = getattr(image, 'codepoint')
- self._replace_char_at_iter(iter_, codepoint)
- image.destroy()
-
- iter_ = self.get_buffer().get_start_iter()
- _replace(iter_.get_child_anchor())
-
- while iter_.forward_char():
- _replace(iter_.get_child_anchor())
-
- def _replace_char_at_iter(self,
- iter_: Gtk.TextIter,
- new_char: str
- ) -> None:
- buf = self.get_buffer()
- iter_2 = iter_.copy()
- iter_2.forward_char()
- buf.delete(iter_, iter_2)
- buf.insert(iter_, new_char)
-
- def insert_emoji(self, codepoint: str, pixbuf: GdkPixbuf.Pixbuf) -> None:
- buf = self.get_buffer()
- if buf.get_char_count():
- # buffer contains text
- buf.insert_at_cursor(' ')
-
- insert_mark = buf.get_insert()
- insert_iter = buf.get_iter_at_mark(insert_mark)
-
- if pixbuf is None:
- buf.insert(insert_iter, codepoint)
- else:
- anchor = buf.create_child_anchor(insert_iter)
- image = Gtk.Image.new_from_pixbuf(pixbuf)
- setattr(image, 'codepoint', codepoint)
- image.show()
- self.add_child_at_anchor(image, anchor)
- buf.insert_at_cursor(' ')
-
def clear(self, *args: Any) -> None:
buf = self.get_buffer()
start, end = buf.get_bounds()
=====================================
gajim/gtk/preferences.py
=====================================
@@ -26,7 +26,6 @@
from gajim.common import app
from gajim.common import configpaths
-from gajim.common import helpers
from gajim.common.const import THRESHOLD_OPTIONS
from gajim.common.i18n import _
from gajim.common.helpers import open_file
@@ -42,7 +41,6 @@
from .const import SettingType
from .dialogs import DialogButton
from .dialogs import ConfirmationDialog
-from .emoji_chooser import emoji_chooser
from .settings import PopoverSetting
from .settings import SettingsBox
from .settings import SettingsDialog
@@ -81,8 +79,6 @@ def __init__(self) -> None:
self.add(self._ui.grid)
- self._check_emoji_theme()
-
prefs: list[tuple[str, Type[PreferenceBox]]] = [
('window_behaviour', WindowBehaviour),
('plugins', Plugins),
@@ -95,7 +91,6 @@ def __init__(self) -> None:
('status_message', StatusMessage),
('automatic_status', AutomaticStatus),
('themes', Themes),
- ('emoji', Emoji),
('server', Server),
('audio', Audio),
('video', Video),
@@ -117,10 +112,6 @@ def __init__(self) -> None:
self._ui.connect_signals(self)
self.show_all()
- if sys.platform != 'darwin':
- # TODO: Remove if colored emoji rendering works well on
- # Windows and MacOS
- self._ui.emoji.hide()
def get_ui(self):
return self._ui
@@ -157,14 +148,6 @@ def update_proxy_list(self) -> None:
miscellaneous = cast(Miscellaneous, self._prefs['miscellaneous'])
miscellaneous.update_proxy_list()
- @staticmethod
- def _check_emoji_theme() -> None:
- # Ensure selected emoji theme is valid
- emoji_themes = helpers.get_available_emoticon_themes()
- settings_theme = app.settings.get('emoticons_theme')
- if settings_theme not in emoji_themes:
- app.settings.set('emoticons_theme', 'font')
-
def _on_destroy(self, _widget: Gtk.Widget) -> None:
self._prefs.clear()
app.check_finalize(self)
@@ -704,42 +687,6 @@ def _on_dark_theme(value: str, *args: Any) -> None:
app.ged.raise_event(StyleChanged())
-class Emoji(PreferenceBox):
- def __init__(self, *args: Any) -> None:
- if sys.platform != 'darwin':
- # TODO: Remove if colored emoji rendering works well on
- # Windows and MacOS
- PreferenceBox.__init__(self, [])
- return
-
- emoji_themes_items: list[str] = []
- for theme in helpers.get_available_emoticon_themes():
- emoji_themes_items.append(theme)
-
- settings = [
- Setting(SettingKind.POPOVER,
- _('Emoji Theme'),
- SettingType.CONFIG,
- 'emoticons_theme',
- desc=_('Choose from various emoji styles'),
- props={'entries': emoji_themes_items},
- callback=self._on_emoticons_theme)
- ]
-
- PreferenceBox.__init__(self, settings)
-
- def _on_emoticons_theme(self, *args: Any) -> None:
- emoji_chooser.load()
- self._toggle_emoticons()
-
- @staticmethod
- def _toggle_emoticons() -> None:
- # TODO
- # for ctrl in app.window.get_controls():
- # ctrl.toggle_emoticons()
- pass
-
-
class Server(PreferenceBox):
def __init__(self, *args: Any) -> None:
=====================================
pyproject.toml
=====================================
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
[tool.codespell]
-skip = "*__pycache__*,gajim/gui,gajim/data/icons,gajim/data/sounds,gajim/data/emoticons"
+skip = "*__pycache__*,gajim/gui,gajim/data/icons,gajim/data/sounds"
ignore-words-list = "claus,pres,ser,trough"
=====================================
setup.cfg
=====================================
@@ -49,8 +49,6 @@ install_requires =
[options.package_data]
gajim =
- data/emoticons/*/*.png
- data/emoticons/*/LICENSE
data/gui/*.ui
data/icons/hicolor/*/*/*.png
data/icons/hicolor/*/*/*.svg
View it on GitLab: https://dev.gajim.org/gajim/gajim/-/commit/eebbe3e87fa5904f94951af0161262c6…
--
View it on GitLab: https://dev.gajim.org/gajim/gajim/-/commit/eebbe3e87fa5904f94951af0161262c6…
You're receiving this email because of your account on dev.gajim.org.
Philipp Hörist pushed to branch remove-old-emoji-code at gajim / gajim
Commits:
b84ef377 by mjk at 2022-11-30T21:05:07+00:00
imprv: Styling: Introduce URI scheme whitelisting
Fixes #11266
Co-authored-by: Philipp Hörist <philipp(a)hoerist.com>
- - - - -
ec0008b7 by mjk at 2022-11-30T21:17:13+00:00
fix: Styling: Linkify URIs only if at the start of a word
- - - - -
88989297 by mjk at 2022-11-30T21:19:03+00:00
refactor: Tests: Improve failure reports in test_styling
- - - - -
eebbe3e8 by wurstsalat at 2022-11-30T21:34:12+00:00
refactor: Remove old emoji code
Fixes #10932
- - - - -
28 changed files:
- .gitlab/issue_templates/Release.md
- gajim/common/configpaths.py
- gajim/common/const.py
- gajim/common/helpers.py
- + gajim/common/iana.py
- gajim/common/setting_values.py
- gajim/common/settings.py
- gajim/common/styling.py
- − gajim/data/emoticons/noto/LICENSE
- − gajim/data/emoticons/noto/noto.png
- − gajim/data/emoticons/twemoji/LICENSE
- − gajim/data/emoticons/twemoji/twemoji.png
- gajim/data/gui/message_actions_box.ui
- gajim/data/gui/preferences.ui
- gajim/data/style/gajim.css
- gajim/gtk/application.py
- gajim/gtk/chat_stack.py
- gajim/gtk/conversation/plain_widget.py
- − gajim/gtk/emoji_chooser.py
- − gajim/gtk/emoji_data.py
- − gajim/gtk/emoji_data.pyi
- gajim/gtk/message_actions_box.py
- gajim/gtk/message_input.py
- gajim/gtk/preferences.py
- pyproject.toml
- + scripts/get_iana_data.py
- setup.cfg
- test/no_gui/test_styling.py
Changes:
=====================================
.gitlab/issue_templates/Release.md
=====================================
@@ -2,6 +2,7 @@
## Build
* [ ] Merge translations from Weblate
+* [ ] Update IANA data with `./scripts/get_iana_data.py gajim/common/iana.py`
* [ ] Run `./scripts/bump_version.py x.x.x`
* [ ] Push release tag `x.x.x`
=====================================
gajim/common/configpaths.py
=====================================
@@ -125,7 +125,6 @@ def __init__(self) -> None:
source_paths = [
('DATA', basedir / 'data'),
('STYLE', basedir / 'data' / 'style'),
- ('EMOTICONS', basedir / 'data' / 'emoticons'),
('GUI', basedir / 'data' / 'gui'),
('ICONS', basedir / 'data' / 'icons'),
('HOME', Path.home()),
@@ -216,8 +215,6 @@ def init(self):
('PLUGINS_IMAGES', 'plugins_images',
PathLocation.CACHE, PathType.FOLDER),
('PLUGINS_USER', 'plugins', PathLocation.DATA, PathType.FOLDER),
- ('MY_EMOTS',
- 'emoticons', PathLocation.DATA, PathType.FOLDER_OPTIONAL),
('MY_ICONSETS',
'iconsets', PathLocation.DATA, PathType.FOLDER_OPTIONAL),
=====================================
gajim/common/const.py
=====================================
@@ -944,97 +944,6 @@ def is_active(self):
GAJIM_WIKI_URI = 'https://dev.gajim.org/gajim/gajim/wikis'
GAJIM_SUPPORT_JID = 'gajim(a)conference.gajim.org'
-URI_SCHEMES = {
- 'aaa://',
- 'aaas://',
- 'acap://',
- 'cap://',
- 'cid:',
- 'crid://',
- 'data:',
- 'dav:',
- 'dict://',
- 'dns:',
- 'fax:',
- 'file:/',
- 'ftp://',
- 'geo:',
- 'go:',
- 'gopher://',
- 'h323:',
- 'http://',
- 'https://',
- 'iax:',
- 'icap://',
- 'im:',
- 'imap://',
- 'info:',
- 'ipp://',
- 'iris:',
- 'iris.beep:',
- 'iris.xpc:',
- 'iris.xpcs:',
- 'iris.lwz:',
- 'ldap://',
- 'mailto:',
- 'mid:',
- 'modem:',
- 'msrp://',
- 'msrps://',
- 'mtqp://',
- 'mupdate://',
- 'news:',
- 'nfs://',
- 'nntp://',
- 'opaquelocktoken:',
- 'pop://',
- 'pres:',
- 'prospero://',
- 'rtsp://',
- 'service:',
- 'sip:',
- 'sips:',
- 'sms:',
- 'snmp://',
- 'soap.beep://',
- 'soap.beeps://',
- 'tag:',
- 'tel:',
- 'telnet://',
- 'tftp://',
- 'thismessage:/',
- 'tip://',
- 'tv:',
- 'urn://',
- 'vemmi://',
- 'xmlrpc.beep://',
- 'xmlrpc.beeps://',
- 'xmpp:',
- 'z39.50r://',
- 'z39.50s://',
- 'about:',
- 'apt:',
- 'cvs://',
- 'daap://',
- 'ed2k://',
- 'feed:',
- 'fish://',
- 'git://',
- 'iax2:',
- 'irc://',
- 'ircs://',
- 'ldaps://',
- 'magnet:',
- 'mms://',
- 'rsync://',
- 'ssh://',
- 'svn://',
- 'sftp://',
- 'smb://',
- 'webcal://',
- 'aesgcm://',
-}
-
# This is an excerpt of Media Types from
# https://www.iana.org/assignments/media-types/media-types.xhtml
=====================================
gajim/common/helpers.py
=====================================
@@ -87,6 +87,7 @@
from gajim.common import app
from gajim.common import configpaths
+from gajim.common import iana
from gajim.common.i18n import Q_
from gajim.common.i18n import _
from gajim.common.i18n import ngettext
@@ -765,22 +766,6 @@ def version_condition(current_version: str, required_version: str) -> bool:
return True
-def get_available_emoticon_themes() -> list[str]:
- files: list[Path] = []
- for folder in configpaths.get('EMOTICONS').iterdir():
- if not folder.is_dir():
- continue
- files += [theme for theme in folder.iterdir() if theme.is_file()]
-
- my_emots = configpaths.get('MY_EMOTS')
- if my_emots.is_dir():
- files += list(my_emots.iterdir())
-
- emoticons_themes = ['font']
- emoticons_themes += [file.stem for file in files if file.suffix == '.png']
- return sorted(emoticons_themes)
-
-
def call_counter(func):
def helper(self, restart=False):
if restart:
@@ -922,6 +907,12 @@ def func_wrapper(self, *args, **kwargs):
return func_wrapper
+def is_registered_uri_scheme(scheme: str) -> bool:
+ if scheme in iana.URI_SCHEMES:
+ return True
+ return scheme in app.settings.get('additional_uri_schemes').split()
+
+
def parse_xmpp_uri_query(pct_iquerycomp: str) -> tuple[str, dict[str, str]]:
'''
Parses 'mess%61ge;b%6Fdy=Hello%20%F0%9F%8C%90%EF%B8%8F' into
@@ -959,6 +950,9 @@ def parse_uri(uri: str) -> URI:
scheme = urlparts.scheme # urlparse is expected to return it in lower case
+ if not is_registered_uri_scheme(scheme):
+ return URI(URIType.INVALID, uri, data={'error': 'Unregistered scheme'})
+
if scheme in ('https', 'http'):
if not urlparts.netloc:
err = f'No host or empty host in an {scheme} URI'
=====================================
gajim/common/iana.py
=====================================
@@ -0,0 +1,368 @@
+# Generated by get_iana_data.py @ 2022-11-30T19:55:30.680829
+
+URI_SCHEMES = {
+ 'aaa',
+ 'aaas',
+ 'about',
+ 'acap',
+ 'acct',
+ 'acd',
+ 'acr',
+ 'adiumxtra',
+ 'adt',
+ 'afp',
+ 'afs',
+ 'aim',
+ 'amss',
+ 'android',
+ 'appdata',
+ 'apt',
+ 'ar',
+ 'ark',
+ 'attachment',
+ 'aw',
+ 'barion',
+ 'bb',
+ 'beshare',
+ 'bitcoin',
+ 'bitcoincash',
+ 'blob',
+ 'bolo',
+ 'browserext',
+ 'cabal',
+ 'calculator',
+ 'callto',
+ 'cap',
+ 'cast',
+ 'casts',
+ 'chrome',
+ 'chrome-extension',
+ 'cid',
+ 'coap',
+ 'coap+tcp',
+ 'coap+ws',
+ 'coaps',
+ 'coaps+tcp',
+ 'coaps+ws',
+ 'com-eventbrite-attendee',
+ 'content',
+ 'content-type',
+ 'crid',
+ 'cstr',
+ 'cvs',
+ 'dab',
+ 'dat',
+ 'data',
+ 'dav',
+ 'diaspora',
+ 'dict',
+ 'did',
+ 'dis',
+ 'dlna-playcontainer',
+ 'dlna-playsingle',
+ 'dns',
+ 'dntp',
+ 'doi',
+ 'dpp',
+ 'drm',
+ 'drop',
+ 'dtmi',
+ 'dtn',
+ 'dvb',
+ 'dvx',
+ 'dweb',
+ 'ed2k',
+ 'eid',
+ 'elsi',
+ 'embedded',
+ 'ens',
+ 'ethereum',
+ 'example',
+ 'facetime',
+ 'fax',
+ 'feed',
+ 'feedready',
+ 'fido',
+ 'file',
+ 'filesystem',
+ 'finger',
+ 'first-run-pen-experience',
+ 'fish',
+ 'fm',
+ 'ftp',
+ 'fuchsia-pkg',
+ 'geo',
+ 'gg',
+ 'git',
+ 'gitoid',
+ 'gizmoproject',
+ 'go',
+ 'gopher',
+ 'graph',
+ 'grd',
+ 'gtalk',
+ 'h323',
+ 'ham',
+ 'hcap',
+ 'hcp',
+ 'http',
+ 'https',
+ 'hxxp',
+ 'hxxps',
+ 'hydrazone',
+ 'hyper',
+ 'iax',
+ 'icap',
+ 'icon',
+ 'im',
+ 'imap',
+ 'info',
+ 'iotdisco',
+ 'ipfs',
+ 'ipn',
+ 'ipns',
+ 'ipp',
+ 'ipps',
+ 'irc',
+ 'irc6',
+ 'ircs',
+ 'iris',
+ 'iris.beep',
+ 'iris.lwz',
+ 'iris.xpc',
+ 'iris.xpcs',
+ 'isostore',
+ 'itms',
+ 'jabber',
+ 'jar',
+ 'jms',
+ 'keyparc',
+ 'lastfm',
+ 'lbry',
+ 'ldap',
+ 'ldaps',
+ 'leaptofrogans',
+ 'lorawan',
+ 'lpa',
+ 'lvlt',
+ 'magnet',
+ 'mailserver',
+ 'mailto',
+ 'maps',
+ 'market',
+ 'matrix',
+ 'message',
+ 'microsoft.windows.camera',
+ 'microsoft.windows.camera.multipicker',
+ 'microsoft.windows.camera.picker',
+ 'mid',
+ 'mms',
+ 'modem',
+ 'mongodb',
+ 'moz',
+ 'ms-access',
+ 'ms-appinstaller',
+ 'ms-browser-extension',
+ 'ms-calculator',
+ 'ms-drive-to',
+ 'ms-enrollment',
+ 'ms-excel',
+ 'ms-eyecontrolspeech',
+ 'ms-gamebarservices',
+ 'ms-gamingoverlay',
+ 'ms-getoffice',
+ 'ms-help',
+ 'ms-infopath',
+ 'ms-inputapp',
+ 'ms-lockscreencomponent-config',
+ 'ms-media-stream-id',
+ 'ms-meetnow',
+ 'ms-mixedrealitycapture',
+ 'ms-mobileplans',
+ 'ms-newsandinterests',
+ 'ms-officeapp',
+ 'ms-people',
+ 'ms-project',
+ 'ms-powerpoint',
+ 'ms-publisher',
+ 'ms-restoretabcompanion',
+ 'ms-screenclip',
+ 'ms-screensketch',
+ 'ms-search',
+ 'ms-search-repair',
+ 'ms-secondary-screen-controller',
+ 'ms-secondary-screen-setup',
+ 'ms-settings',
+ 'ms-settings-airplanemode',
+ 'ms-settings-bluetooth',
+ 'ms-settings-camera',
+ 'ms-settings-cellular',
+ 'ms-settings-cloudstorage',
+ 'ms-settings-connectabledevices',
+ 'ms-settings-displays-topology',
+ 'ms-settings-emailandaccounts',
+ 'ms-settings-language',
+ 'ms-settings-location',
+ 'ms-settings-lock',
+ 'ms-settings-nfctransactions',
+ 'ms-settings-notifications',
+ 'ms-settings-power',
+ 'ms-settings-privacy',
+ 'ms-settings-proximity',
+ 'ms-settings-screenrotation',
+ 'ms-settings-wifi',
+ 'ms-settings-workplace',
+ 'ms-spd',
+ 'ms-stickers',
+ 'ms-sttoverlay',
+ 'ms-transit-to',
+ 'ms-useractivityset',
+ 'ms-virtualtouchpad',
+ 'ms-visio',
+ 'ms-walk-to',
+ 'ms-whiteboard',
+ 'ms-whiteboard-cmd',
+ 'ms-word',
+ 'msnim',
+ 'msrp',
+ 'msrps',
+ 'mss',
+ 'mt',
+ 'mtqp',
+ 'mumble',
+ 'mupdate',
+ 'mvn',
+ 'news',
+ 'nfs',
+ 'ni',
+ 'nih',
+ 'nntp',
+ 'notes',
+ 'num',
+ 'ocf',
+ 'oid',
+ 'onenote',
+ 'onenote-cmd',
+ 'opaquelocktoken',
+ 'openpgp4fpr',
+ 'otpauth',
+ 'p1',
+ 'pack',
+ 'palm',
+ 'paparazzi',
+ 'payment',
+ 'payto',
+ 'pkcs11',
+ 'platform',
+ 'pop',
+ 'pres',
+ 'prospero',
+ 'proxy',
+ 'pwid',
+ 'psyc',
+ 'pttp',
+ 'qb',
+ 'query',
+ 'quic-transport',
+ 'redis',
+ 'rediss',
+ 'reload',
+ 'res',
+ 'resource',
+ 'rmi',
+ 'rsync',
+ 'rtmfp',
+ 'rtmp',
+ 'rtsp',
+ 'rtsps',
+ 'rtspu',
+ 'sarif',
+ 'secondlife',
+ 'secret-token',
+ 'service',
+ 'session',
+ 'sftp',
+ 'sgn',
+ 'shc',
+ 'shttp',
+ 'sieve',
+ 'simpleledger',
+ 'simplex',
+ 'sip',
+ 'sips',
+ 'skype',
+ 'smb',
+ 'smp',
+ 'sms',
+ 'smtp',
+ 'snews',
+ 'snmp',
+ 'soap.beep',
+ 'soap.beeps',
+ 'soldat',
+ 'spiffe',
+ 'spotify',
+ 'ssb',
+ 'ssh',
+ 'steam',
+ 'stun',
+ 'stuns',
+ 'submit',
+ 'svn',
+ 'swh',
+ 'swid',
+ 'swidpath',
+ 'tag',
+ 'taler',
+ 'teamspeak',
+ 'tel',
+ 'teliaeid',
+ 'telnet',
+ 'tftp',
+ 'things',
+ 'thismessage',
+ 'tip',
+ 'tn3270',
+ 'tool',
+ 'turn',
+ 'turns',
+ 'tv',
+ 'udp',
+ 'unreal',
+ 'upt',
+ 'urn',
+ 'ut2004',
+ 'uuid-in-package',
+ 'v-event',
+ 'vemmi',
+ 'ventrilo',
+ 'ves',
+ 'videotex',
+ 'vnc',
+ 'view-source',
+ 'vscode',
+ 'vscode-insiders',
+ 'vsls',
+ 'w3',
+ 'wais',
+ 'web3',
+ 'wcr',
+ 'webcal',
+ 'wifi',
+ 'wpid',
+ 'ws',
+ 'wss',
+ 'wtai',
+ 'wyciwyg',
+ 'xcon',
+ 'xcon-userid',
+ 'xfire',
+ 'xmlrpc.beep',
+ 'xmlrpc.beeps',
+ 'xmpp',
+ 'xri',
+ 'ymsgr',
+ 'z39.50',
+ 'z39.50r',
+ 'z39.50s',
+}
=====================================
gajim/common/setting_values.py
=====================================
@@ -36,6 +36,7 @@ class _ACCOUNT_DEFAULT:
BoolSettings = Literal[
+ 'additional_uri_schemes',
'allow_open_file_uris',
'always_english_wikipedia',
'always_english_wiktionary',
@@ -48,7 +49,6 @@ class _ACCOUNT_DEFAULT:
'confirm_close_muc',
'confirm_on_window_delete',
'dev_force_bookmark_2',
- 'dev_use_message_label',
'developer_modus',
'enable_emoji_shortcodes',
'enable_negative_priority',
@@ -136,7 +136,6 @@ class _ACCOUNT_DEFAULT:
'date_format',
'date_time_format',
'dictionary_url',
- 'emoticons_theme',
'gc_refer_to_nick_char',
'global_proxy',
'last_save_dir',
@@ -164,6 +163,7 @@ class _ACCOUNT_DEFAULT:
AllSettingsT = Union[str, int, bool, list[str]]
APP_SETTINGS = {
+ 'additional_uri_schemes': '',
'allow_open_file_uris': False,
'always_english_wikipedia': False,
'always_english_wiktionary': True,
@@ -192,7 +192,6 @@ class _ACCOUNT_DEFAULT:
'dev_use_message_label': True,
'developer_modus': False,
'dictionary_url': 'WIKTIONARY',
- 'emoticons_theme': 'noto-emoticons',
'enable_emoji_shortcodes': True,
'enable_negative_priority': False,
'enable_file_preview': True,
@@ -628,6 +627,9 @@ class WorkspaceSettings(TypedDict):
ADVANCED_SETTINGS = {
'app': {
+ 'additional_uri_schemes': _(
+ 'Clickable schemes in addition to the hard-coded list of '
+ 'IANA-registered ones. Space-separated, lower-case, no colons.'),
'allow_open_file_uris': _('Allows opening file:/ URIs when clicked.'),
'always_english_wikipedia': '',
'always_english_wiktionary': '',
=====================================
gajim/common/settings.py
=====================================
@@ -237,11 +237,6 @@ def _setup_installation_defaults() -> None:
if IS_PORTABLE:
APP_SETTINGS['use_keyring'] = False
- if sys.platform in ('win32', 'darwin'):
- # Cairo does not support color emojis fonts on
- # Windows and MacOS
- APP_SETTINGS['dev_use_message_label'] = False
-
def _load_app_overrides(self) -> None:
if not OVERRIDES_PATH.exists():
return
=====================================
gajim/common/styling.py
=====================================
@@ -19,7 +19,6 @@
from typing import Optional
import string
-import sys
import re
from dataclasses import dataclass
from dataclasses import field
@@ -31,7 +30,6 @@
from gajim.common.helpers import parse_uri as analyze_uri
from gajim.common.helpers import validate_jid
from gajim.common.text_helpers import jid_to_iri
-from gajim.gui.emoji_data import emoji_data
PRE = '`'
STRONG = '*'
@@ -50,10 +48,8 @@
BLOCK_NESTED_RX = re.compile(PRE_NESTED_RX + '|' + QUOTE_RX, re.S | re.M)
UNQUOTE_RX = re.compile(r'^> |^>', re.M)
-URI_OR_JID_RX = re.compile(fr'(?P<uri>{regex.IRI})|(?P<jid>{regex.XMPP.jid})')
-
-EMOJI_RX = emoji_data.get_regex()
-EMOJI_RX = re.compile(EMOJI_RX)
+URI_OR_JID_RX = re.compile(
+ fr'(?P<uri>(?<![\w+.-]){regex.IRI})|(?P<jid>{regex.XMPP.jid})')
SD = 0
SD_POS = 1
@@ -96,11 +92,6 @@ class MailAddress(BaseHyperlink):
name: str = field(default='mailadr', init=False)
-@dataclass
-class Emoji(StyleObject):
- name: str = field(default='emoji', init=False)
-
-
@dataclass
class Block(StyleObject):
@@ -116,7 +107,6 @@ class PlainBlock(Block):
name: str = field(default='plain', init=False)
spans: list[Span] = field(default_factory=list)
uris: list[BaseHyperlink] = field(default_factory=list)
- emojis: list[Emoji] = field(default_factory=list)
@classmethod
def from_quote_match(cls, match: Match[str]) -> PlainBlock:
@@ -207,10 +197,6 @@ def process(text: Union[str, bytes], level: int = 0) -> ParsingResult:
for line in block.text.splitlines(keepends=True):
block.spans += _parse_line(line, offset, offset_bytes)
block.uris += _parse_uris(line, offset, offset_bytes)
- if sys.platform == 'darwin':
- # block.emojis is used for replacing emojis with Gtk.Images
- # Necessary for MessageTextview (darwin) only
- block.emojis += _parse_emojis(line, offset)
offset += len(line)
offset_bytes += len(line.encode())
@@ -360,22 +346,6 @@ def make(start: int, end: int, is_jid: bool) -> Optional[BaseHyperlink]:
return uris
-def _parse_emojis(line: str, offset: int) -> list[Emoji]:
- emojis: list[Emoji] = []
-
- def make(match: Match[str]) -> Emoji:
- return _make_emoji(line,
- match.start(),
- match.end() - 1,
- offset)
-
- for match in EMOJI_RX.finditer(line):
- emoji = make(match)
- emojis.append(emoji)
-
- return emojis
-
-
def _handle_pre_span(line: str,
index: int,
offset: int,
@@ -463,18 +433,6 @@ def _make_hyperlink(line: str,
text=text)
-def _make_emoji(line: str,
- start: int,
- end: int,
- offset: int) -> Emoji:
- text = line[start:end + 1]
- start += offset
- end += offset + 1
- return Emoji(start=start,
- end=end,
- text=text)
-
-
def _is_span_empty(sd: str, index: int, stack: list[tuple[str, int]]) -> bool:
start_sd = stack[-1][SD]
if start_sd != sd:
=====================================
gajim/data/emoticons/noto/LICENSE deleted
=====================================
@@ -1,201 +0,0 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
=====================================
gajim/data/emoticons/noto/noto.png deleted
=====================================
Binary files a/gajim/data/emoticons/noto/noto.png and /dev/null differ
=====================================
gajim/data/emoticons/twemoji/LICENSE deleted
=====================================
@@ -1,393 +0,0 @@
-Attribution 4.0 International
-
-=======================================================================
-
-Creative Commons Corporation ("Creative Commons") is not a law firm and
-does not provide legal services or legal advice. Distribution of
-Creative Commons public licenses does not create a lawyer-client or
-other relationship. Creative Commons makes its licenses and related
-information available on an "as-is" basis. Creative Commons gives no
-warranties regarding its licenses, any material licensed under their
-terms and conditions, or any related information. Creative Commons
-disclaims all liability for damages resulting from their use to the
-fullest extent possible.
-
-Using Creative Commons Public Licenses
-
-Creative Commons public licenses provide a standard set of terms and
-conditions that creators and other rights holders may use to share
-original works of authorship and other material subject to copyright
-and certain other rights specified in the public license below. The
-following considerations are for informational purposes only, are not
-exhaustive, and do not form part of our licenses.
-
- Considerations for licensors: Our public licenses are
- intended for use by those authorized to give the public
- permission to use material in ways otherwise restricted by
- copyright and certain other rights. Our licenses are
- irrevocable. Licensors should read and understand the terms
- and conditions of the license they choose before applying it.
- Licensors should also secure all rights necessary before
- applying our licenses so that the public can reuse the
- material as expected. Licensors should clearly mark any
- material not subject to the license. This includes other CC-
- licensed material, or material used under an exception or
- limitation to copyright. More considerations for licensors:
- wiki.creativecommons.org/Considerations_for_licensors
-
- Considerations for the public: By using one of our public
- licenses, a licensor grants the public permission to use the
- licensed material under specified terms and conditions. If
- the licensor's permission is not necessary for any reason--for
- example, because of any applicable exception or limitation to
- copyright--then that use is not regulated by the license. Our
- licenses grant only permissions under copyright and certain
- other rights that a licensor has authority to grant. Use of
- the licensed material may still be restricted for other
- reasons, including because others have copyright or other
- rights in the material. A licensor may make special requests,
- such as asking that all changes be marked or described.
- Although not required by our licenses, you are encouraged to
- respect those requests where reasonable. More_considerations
- for the public:
- wiki.creativecommons.org/Considerations_for_licensees
-
-=======================================================================
-
-Creative Commons Attribution 4.0 International Public License
-
-By exercising the Licensed Rights (defined below), You accept and agree
-to be bound by the terms and conditions of this Creative Commons
-Attribution 4.0 International Public License ("Public License"). To the
-extent this Public License may be interpreted as a contract, You are
-granted the Licensed Rights in consideration of Your acceptance of
-these terms and conditions, and the Licensor grants You such rights in
-consideration of benefits the Licensor receives from making the
-Licensed Material available under these terms and conditions.
-
-
-Section 1 -- Definitions.
-
- a. Adapted Material means material subject to Copyright and Similar
- Rights that is derived from or based upon the Licensed Material
- and in which the Licensed Material is translated, altered,
- arranged, transformed, or otherwise modified in a manner requiring
- permission under the Copyright and Similar Rights held by the
- Licensor. For purposes of this Public License, where the Licensed
- Material is a musical work, performance, or sound recording,
- Adapted Material is always produced where the Licensed Material is
- synched in timed relation with a moving image.
-
- b. Adapter's License means the license You apply to Your Copyright
- and Similar Rights in Your contributions to Adapted Material in
- accordance with the terms and conditions of this Public License.
-
- c. Copyright and Similar Rights means copyright and/or similar rights
- closely related to copyright including, without limitation,
- performance, broadcast, sound recording, and Sui Generis Database
- Rights, without regard to how the rights are labeled or
- categorized. For purposes of this Public License, the rights
- specified in Section 2(b)(1)-(2) are not Copyright and Similar
- Rights.
-
- d. Effective Technological Measures means those measures that, in the
- absence of proper authority, may not be circumvented under laws
- fulfilling obligations under Article 11 of the WIPO Copyright
- Treaty adopted on December 20, 1996, and/or similar international
- agreements.
-
- e. Exceptions and Limitations means fair use, fair dealing, and/or
- any other exception or limitation to Copyright and Similar Rights
- that applies to Your use of the Licensed Material.
-
- f. Licensed Material means the artistic or literary work, database,
- or other material to which the Licensor applied this Public
- License.
-
- g. Licensed Rights means the rights granted to You subject to the
- terms and conditions of this Public License, which are limited to
- all Copyright and Similar Rights that apply to Your use of the
- Licensed Material and that the Licensor has authority to license.
-
- h. Licensor means the individual(s) or entity(ies) granting rights
- under this Public License.
-
- i. Share means to provide material to the public by any means or
- process that requires permission under the Licensed Rights, such
- as reproduction, public display, public performance, distribution,
- dissemination, communication, or importation, and to make material
- available to the public including in ways that members of the
- public may access the material from a place and at a time
- individually chosen by them.
-
- j. Sui Generis Database Rights means rights other than copyright
- resulting from Directive 96/9/EC of the European Parliament and of
- the Council of 11 March 1996 on the legal protection of databases,
- as amended and/or succeeded, as well as other essentially
- equivalent rights anywhere in the world.
-
- k. You means the individual or entity exercising the Licensed Rights
- under this Public License. Your has a corresponding meaning.
-
-
-Section 2 -- Scope.
-
- a. License grant.
-
- 1. Subject to the terms and conditions of this Public License,
- the Licensor hereby grants You a worldwide, royalty-free,
- non-sublicensable, non-exclusive, irrevocable license to
- exercise the Licensed Rights in the Licensed Material to:
-
- a. reproduce and Share the Licensed Material, in whole or
- in part; and
-
- b. produce, reproduce, and Share Adapted Material.
-
- 2. Exceptions and Limitations. For the avoidance of doubt, where
- Exceptions and Limitations apply to Your use, this Public
- License does not apply, and You do not need to comply with
- its terms and conditions.
-
- 3. Term. The term of this Public License is specified in Section
- 6(a).
-
- 4. Media and formats; technical modifications allowed. The
- Licensor authorizes You to exercise the Licensed Rights in
- all media and formats whether now known or hereafter created,
- and to make technical modifications necessary to do so. The
- Licensor waives and/or agrees not to assert any right or
- authority to forbid You from making technical modifications
- necessary to exercise the Licensed Rights, including
- technical modifications necessary to circumvent Effective
- Technological Measures. For purposes of this Public License,
- simply making modifications authorized by this Section 2(a)
- (4) never produces Adapted Material.
-
- 5. Downstream recipients.
-
- a. Offer from the Licensor -- Licensed Material. Every
- recipient of the Licensed Material automatically
- receives an offer from the Licensor to exercise the
- Licensed Rights under the terms and conditions of this
- Public License.
-
- b. No downstream restrictions. You may not offer or impose
- any additional or different terms or conditions on, or
- apply any Effective Technological Measures to, the
- Licensed Material if doing so restricts exercise of the
- Licensed Rights by any recipient of the Licensed
- Material.
-
- 6. No endorsement. Nothing in this Public License constitutes or
- may be construed as permission to assert or imply that You
- are, or that Your use of the Licensed Material is, connected
- with, or sponsored, endorsed, or granted official status by,
- the Licensor or others designated to receive attribution as
- provided in Section 3(a)(1)(A)(i).
-
- b. Other rights.
-
- 1. Moral rights, such as the right of integrity, are not
- licensed under this Public License, nor are publicity,
- privacy, and/or other similar personality rights; however, to
- the extent possible, the Licensor waives and/or agrees not to
- assert any such rights held by the Licensor to the limited
- extent necessary to allow You to exercise the Licensed
- Rights, but not otherwise.
-
- 2. Patent and trademark rights are not licensed under this
- Public License.
-
- 3. To the extent possible, the Licensor waives any right to
- collect royalties from You for the exercise of the Licensed
- Rights, whether directly or through a collecting society
- under any voluntary or waivable statutory or compulsory
- licensing scheme. In all other cases the Licensor expressly
- reserves any right to collect such royalties.
-
-
-Section 3 -- License Conditions.
-
-Your exercise of the Licensed Rights is expressly made subject to the
-following conditions.
-
- a. Attribution.
-
- 1. If You Share the Licensed Material (including in modified
- form), You must:
-
- a. retain the following if it is supplied by the Licensor
- with the Licensed Material:
-
- i. identification of the creator(s) of the Licensed
- Material and any others designated to receive
- attribution, in any reasonable manner requested by
- the Licensor (including by pseudonym if
- designated);
-
- ii. a copyright notice;
-
- iii. a notice that refers to this Public License;
-
- iv. a notice that refers to the disclaimer of
- warranties;
-
- v. a URI or hyperlink to the Licensed Material to the
- extent reasonably practicable;
-
- b. indicate if You modified the Licensed Material and
- retain an indication of any previous modifications; and
-
- c. indicate the Licensed Material is licensed under this
- Public License, and include the text of, or the URI or
- hyperlink to, this Public License.
-
- 2. You may satisfy the conditions in Section 3(a)(1) in any
- reasonable manner based on the medium, means, and context in
- which You Share the Licensed Material. For example, it may be
- reasonable to satisfy the conditions by providing a URI or
- hyperlink to a resource that includes the required
- information.
-
- 3. If requested by the Licensor, You must remove any of the
- information required by Section 3(a)(1)(A) to the extent
- reasonably practicable.
-
- 4. If You Share Adapted Material You produce, the Adapter's
- License You apply must not prevent recipients of the Adapted
- Material from complying with this Public License.
-
-
-Section 4 -- Sui Generis Database Rights.
-
-Where the Licensed Rights include Sui Generis Database Rights that
-apply to Your use of the Licensed Material:
-
- a. for the avoidance of doubt, Section 2(a)(1) grants You the right
- to extract, reuse, reproduce, and Share all or a substantial
- portion of the contents of the database;
-
- b. if You include all or a substantial portion of the database
- contents in a database in which You have Sui Generis Database
- Rights, then the database in which You have Sui Generis Database
- Rights (but not its individual contents) is Adapted Material; and
-
- c. You must comply with the conditions in Section 3(a) if You Share
- all or a substantial portion of the contents of the database.
-
-For the avoidance of doubt, this Section 4 supplements and does not
-replace Your obligations under this Public License where the Licensed
-Rights include other Copyright and Similar Rights.
-
-
-Section 5 -- Disclaimer of Warranties and Limitation of Liability.
-
- a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
- EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
- AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
- ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
- IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
- WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
- PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
- ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
- KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
- ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
-
- b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
- TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
- NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
- INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
- COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
- USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
- ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
- DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
- IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
-
- c. The disclaimer of warranties and limitation of liability provided
- above shall be interpreted in a manner that, to the extent
- possible, most closely approximates an absolute disclaimer and
- waiver of all liability.
-
-
-Section 6 -- Term and Termination.
-
- a. This Public License applies for the term of the Copyright and
- Similar Rights licensed here. However, if You fail to comply with
- this Public License, then Your rights under this Public License
- terminate automatically.
-
- b. Where Your right to use the Licensed Material has terminated under
- Section 6(a), it reinstates:
-
- 1. automatically as of the date the violation is cured, provided
- it is cured within 30 days of Your discovery of the
- violation; or
-
- 2. upon express reinstatement by the Licensor.
-
- For the avoidance of doubt, this Section 6(b) does not affect any
- right the Licensor may have to seek remedies for Your violations
- of this Public License.
-
- c. For the avoidance of doubt, the Licensor may also offer the
- Licensed Material under separate terms or conditions or stop
- distributing the Licensed Material at any time; however, doing so
- will not terminate this Public License.
-
- d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
- License.
-
-
-Section 7 -- Other Terms and Conditions.
-
- a. The Licensor shall not be bound by any additional or different
- terms or conditions communicated by You unless expressly agreed.
-
- b. Any arrangements, understandings, or agreements regarding the
- Licensed Material not stated herein are separate from and
- independent of the terms and conditions of this Public License.
-
-
-Section 8 -- Interpretation.
-
- a. For the avoidance of doubt, this Public License does not, and
- shall not be interpreted to, reduce, limit, restrict, or impose
- conditions on any use of the Licensed Material that could lawfully
- be made without permission under this Public License.
-
- b. To the extent possible, if any provision of this Public License is
- deemed unenforceable, it shall be automatically reformed to the
- minimum extent necessary to make it enforceable. If the provision
- cannot be reformed, it shall be severed from this Public License
- without affecting the enforceability of the remaining terms and
- conditions.
-
- c. No term or condition of this Public License will be waived and no
- failure to comply consented to unless expressly agreed to by the
- Licensor.
-
- d. Nothing in this Public License constitutes or may be interpreted
- as a limitation upon, or waiver of, any privileges and immunities
- that apply to the Licensor or You, including from the legal
- processes of any jurisdiction or authority.
-
-
-=======================================================================
-
-Creative Commons is not a party to its public licenses.
-Notwithstanding, Creative Commons may elect to apply one of its public
-licenses to material it publishes and in those instances will be
-considered the "Licensor." Except for the limited purpose of indicating
-that material is shared under a Creative Commons public license or as
-otherwise permitted by the Creative Commons policies published at
-creativecommons.org/policies, Creative Commons does not authorize the
-use of the trademark "Creative Commons" or any other trademark or logo
-of Creative Commons without its prior written consent including,
-without limitation, in connection with any unauthorized modifications
-to any of its public licenses or any other arrangements,
-understandings, or agreements concerning use of licensed material. For
-the avoidance of doubt, this paragraph does not form part of the public
-licenses.
-
-Creative Commons may be contacted at creativecommons.org.
\ No newline at end of file
=====================================
gajim/data/emoticons/twemoji/twemoji.png deleted
=====================================
Binary files a/gajim/data/emoticons/twemoji/twemoji.png and /dev/null differ
=====================================
gajim/data/gui/message_actions_box.ui
=====================================
@@ -143,6 +143,7 @@
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="tooltip-text" translatable="yes">Show a list of emojis</property>
+ <property name="action-name">win.show-emoji-chooser</property>
<property name="relief">none</property>
<child>
<object class="GtkImage">
=====================================
gajim/data/gui/preferences.ui
=====================================
@@ -688,57 +688,7 @@
</packing>
</child>
<child>
- <!-- n-columns=3 n-rows=3 -->
- <object class="GtkGrid" id="emoji">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
- <property name="orientation">vertical</property>
- <property name="row-spacing">12</property>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
- <property name="label" translatable="yes">Emoji</property>
- <property name="xalign">0</property>
- <style>
- <class name="bold"/>
- </style>
- </object>
- <packing>
- <property name="left-attach">0</property>
- <property name="top-attach">0</property>
- </packing>
- </child>
- <child>
- <placeholder/>
- </child>
- <child>
- <placeholder/>
- </child>
- <child>
- <placeholder/>
- </child>
- <child>
- <placeholder/>
- </child>
- <child>
- <placeholder/>
- </child>
- <child>
- <placeholder/>
- </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>
+ <placeholder/>
</child>
</object>
</child>
=====================================
gajim/data/style/gajim.css
=====================================
@@ -748,18 +748,6 @@ #NotificationPopupTitle {
font-weight: bold;
}
-/* Emoticon Popover (ModifierChooser) */
-popover#EmoticonPopover button { background: none; border: none; box-shadow:none; padding: 0px;}
-popover#EmoticonPopover button > label { font-size: 2.0rem; }
-popover#EmoticonPopover flowboxchild > label { font-size: 2.0rem; }
-popover#EmoticonPopover flowboxchild { padding-top: 5px; padding-bottom: 5px; }
-popover#EmoticonPopover scrolledwindow { border: none; }
-popover#EmoticonPopover { padding: 5px; background-color: @theme_unfocused_base_color; }
-
-.emoji-chooser-heading { font-size: 1.15rem; font-weight: bold; padding: 5px;}
-.emoji-chooser-flowbox { padding-left: 5px; padding-right: 11px; }
-.emoji-modifier-chooser-flowbox { padding-left: 5px; }
-
/* Settings widgets ======================================================== */
/* SettingsBox */
=====================================
gajim/gtk/application.py
=====================================
@@ -244,12 +244,6 @@ def _startup(self) -> None:
idle.Monitor.set_interval(app.settings.get('autoawaytime') * 60,
app.settings.get('autoxatime') * 60)
- if sys.platform == 'darwin':
- # TODO: Remove if colored emoji rendering works well on
- # Windows and MacOS
- from gajim.gui.emoji_chooser import emoji_chooser
- emoji_chooser.load()
-
from gajim.gui_interface import Interface
self.interface = Interface()
=====================================
gajim/gtk/chat_stack.py
=====================================
@@ -712,7 +712,6 @@ def _on_function_message(self,
self._chat_control.add_info_message(message)
def _on_send_message(self) -> None:
- self._message_action_box.msg_textview.replace_emojis()
message = self._message_action_box.msg_textview.get_text()
if message.startswith('//'):
# Escape sequence for chat commands
=====================================
gajim/gtk/conversation/plain_widget.py
=====================================
@@ -14,32 +14,19 @@
from __future__ import annotations
-from typing import Any
-
import logging
-import sys
from gi.repository import Gtk
from gi.repository import Pango
from gi.repository import Gdk
-from gi.repository import GdkPixbuf
from gi.repository import GLib
-from gi.repository import GObject
-from gajim.common import app
-from gajim.common.const import StyleAttr
from gajim.common.helpers import open_uri
-from gajim.common.helpers import parse_uri
-from gajim.common.structs import URI
from gajim.common.styling import BaseHyperlink
from gajim.common.styling import PlainBlock
from gajim.common.styling import process_uris
from ..menus import get_conv_action_context_menu
-from ..menus import get_conv_uri_context_menu
-from ..emoji_data import emoji_pixbufs
-from ..emoji_data import get_emoji_pixbuf
-from ..util import get_cursor
from ..util import make_pango_attributes
log = logging.getLogger('gajim.gui.conversaion.plain_widget')
@@ -55,22 +42,11 @@ def __init__(self, account: str, selectable: bool) -> None:
self._account = account
- # We use a Gtk.Textview on MacOS, since there is no support
- # for rendering color fonts (Emojis) on MacOS yet, see:
- # https://gitlab.freedesktop.org/cairo/cairo/-/merge_requests/289
- # https://gitlab.freedesktop.org/cairo/cairo/-/merge_requests/9
- if sys.platform == 'darwin':
- if app.settings.get('dev_use_message_label'):
- self._text_widget = MessageLabel(self._account, selectable)
- else:
- self._text_widget = MessageTextview(self._account)
- else:
- self._text_widget = MessageLabel(self._account, selectable)
+ self._text_widget = MessageLabel(self._account, selectable)
self.add(self._text_widget)
def set_selectable(self, selectable: bool) -> None:
- if isinstance(self._text_widget, MessageLabel):
- self._text_widget.set_selectable(selectable)
+ self._text_widget.set_selectable(selectable)
def add_content(self, block: PlainBlock) -> None:
self._text_widget.print_text_with_styling(block)
@@ -152,295 +128,3 @@ def _on_focus_out(widget: MessageLabel,
_event: Gdk.EventFocus
) -> None:
widget.get_style_context().add_class('transparent-selection')
-
-
-class MessageTextview(Gtk.TextView):
- def __init__(self, account: str) -> None:
- Gtk.TextView.__init__(self)
- self.set_hexpand(True)
- self.set_margin_start(0)
- self.set_margin_end(0)
- self.set_border_width(0)
- self.set_left_margin(0)
- self.set_right_margin(0)
- self.set_has_tooltip(True)
- self.set_editable(False)
- self.set_cursor_visible(False)
- self.set_wrap_mode(Gtk.WrapMode.WORD_CHAR)
-
- self._handlers: dict[int, MessageTextview] = {}
-
- id_ = self.connect('query-tooltip', self._query_tooltip)
- self._handlers[id_] = self
- id_ = self.connect('button-press-event', self._on_button_press)
- self._handlers[id_] = self
- id_ = self.connect('populate-popup', self._on_populate_popup)
- self._handlers[id_] = self
-
- self._account = account
-
- # Used for changing the mouse pointer when hovering clickable URIs
- self._cursor_changed: bool = False
-
- # Keeps text selections for quoting and search actions
- self._selected_text: str = ''
-
- self.get_style_context().add_class('gajim-conversation-text')
-
- # Create Tags
- self._create_url_tags()
- self.get_buffer().create_tag('strong', weight=Pango.Weight.BOLD)
- self.get_buffer().create_tag('emphasis', style=Pango.Style.ITALIC)
- self.get_buffer().create_tag('strike', strikethrough=True)
- self.get_buffer().create_tag('pre', family='monospace')
-
- self.connect('destroy', self._on_destroy)
-
- def _on_destroy(self, *args: Any) -> None:
- for id_, widget in self._handlers.items():
- if widget.handler_is_connected(id_):
- widget.disconnect(id_)
- self._handlers.clear()
-
- def _create_url_tags(self) -> None:
- color = app.css_config.get_value('.gajim-url', StyleAttr.COLOR)
- for name in URI_TAGS:
- tag = self.get_buffer().create_tag(name,
- foreground=color,
- underline=Pango.Underline.SINGLE)
- tag.connect('event', self._on_uri_clicked, tag)
-
- def clear(self) -> None:
- buffer_ = self.get_buffer()
- start, end = buffer_.get_bounds()
- buffer_.delete(start, end)
-
- def get_text(self) -> str:
- buffer_ = self.get_buffer()
- start, end = buffer_.get_bounds()
- return buffer_.get_text(start, end, False)
-
- def print_text_with_styling(self, block: PlainBlock) -> None:
- buffer_ = self.get_buffer()
- buffer_.insert(buffer_.get_start_iter(), block.text.strip())
-
- if len(self.get_text()) > 10000:
- # Limit message styling processing
- return
-
- for span in block.spans:
- start_iter = buffer_.get_iter_at_offset(span.start)
- end_iter = buffer_.get_iter_at_offset(span.end)
- buffer_.apply_tag_by_name(span.name, start_iter, end_iter)
-
- for uri in block.uris:
- start_iter = buffer_.get_iter_at_offset(uri.start)
- end_iter = buffer_.get_iter_at_offset(uri.end)
- buffer_.apply_tag_by_name(uri.name, start_iter, end_iter)
-
- for emoji in block.emojis:
- start_iter = buffer_.get_iter_at_offset(emoji.start)
- end_iter = buffer_.get_iter_at_offset(emoji.end)
- if emoji_pixbufs.complete:
- # Only search for pixbuf if loading is completed
- pixbuf = get_emoji_pixbuf(emoji.text)
- if pixbuf is None:
- buffer_.insert(end_iter, emoji.text)
- else:
- pixbuf = pixbuf.copy()
- anchor = buffer_.create_child_anchor(end_iter)
- anchor.plaintext = emoji.text # type: ignore
- img = Gtk.Image.new_from_pixbuf(pixbuf)
- img.show()
- self.add_child_at_anchor(img, anchor)
- buffer_.delete(
- buffer_.get_iter_at_offset(emoji.start),
- buffer_.get_iter_at_offset(emoji.end))
- else:
- # Set marks and save them so we can replace emojis
- # once loading is complete
- start_mark = buffer_.create_mark(None, end_iter, True)
- buffer_.insert(end_iter, emoji.text)
- end_mark = buffer_.create_mark(None, end_iter, True)
- emoji_pixbufs.append_marks(
- self, start_mark, end_mark, emoji.text)
-
- def replace_emoji(self,
- start_mark: Gtk.TextMark,
- end_mark: Gtk.TextMark,
- pixbuf: GdkPixbuf.Pixbuf,
- codepoint: str
- ) -> None:
- buffer_ = self.get_buffer()
- start_iter = buffer_.get_iter_at_mark(start_mark)
- end_iter = buffer_.get_iter_at_mark(end_mark)
- buffer_.delete(start_iter, end_iter)
-
- anchor = buffer_.create_child_anchor(start_iter)
- anchor.plaintext = codepoint # type: ignore
- img = Gtk.Image.new_from_pixbuf(pixbuf)
- img.show()
- self.add_child_at_anchor(img, anchor)
- buffer_.delete_mark(start_mark)
- buffer_.delete_mark(end_mark)
-
- def add_action_phrase(self, text: str, nickname: str) -> None:
- text = text.replace('/me', f'* {nickname}', 1)
-
- buffer_ = self.get_buffer()
- buffer_.insert(buffer_.get_start_iter(), text.strip())
-
- uris = process_uris(text)
- for uri in uris:
- start_iter = buffer_.get_iter_at_offset(uri.start)
- end_iter = buffer_.get_iter_at_offset(uri.end)
- buffer_.apply_tag_by_name(uri.name, start_iter, end_iter)
-
- start_iter = buffer_.get_start_iter()
- end_iter = buffer_.get_end_iter()
- buffer_.apply_tag_by_name('emphasis', start_iter, end_iter)
-
- def _query_tooltip(self,
- widget: Gtk.TextView,
- x_pos: int,
- y_pos: int,
- _keyboard_mode: bool,
- _tooltip: Gtk.Tooltip
- ) -> bool:
-
- window = widget.get_window(Gtk.TextWindowType.TEXT)
- assert window is not None
- x_pos, y_pos = self.window_to_buffer_coords(
- Gtk.TextWindowType.TEXT, x_pos, y_pos)
-
- iter_ = self.get_iter_at_position(x_pos, y_pos)[1]
- for tag in iter_.get_tags():
- tag_name = tag.get_property('name')
- if tag_name in URI_TAGS:
- window.set_cursor(get_cursor('pointer'))
- self._cursor_changed = True
- return False
-
- if self._cursor_changed:
- window.set_cursor(get_cursor('text'))
- self._cursor_changed = False
- return False
-
- def _on_button_press(self, _widget: Any, event: Gdk.EventButton) -> bool:
- '''
- We don’t open the standard context menu when receiving
- a click on tagged text.
- If it’s untagged text, check if something is selected
- '''
- self._selected_text = ''
-
- if event.button != 3:
- # If it’s not a right click
- return False
-
- x_pos, y_pos = self.window_to_buffer_coords(
- Gtk.TextWindowType.TEXT,
- int(event.x),
- int(event.y))
- _, iter_ = self.get_iter_at_location(x_pos, y_pos)
- tags = iter_.get_tags()
-
- if tags:
- # A tagged text fragment has been clicked
- for tag in tags:
- if tag.get_property('name') in URI_TAGS:
- # Block regular context menu
- return True
-
- # Check if there is a selection and make it available for
- # _on_populate_popup
- buffer_ = self.get_buffer()
- return_val = buffer_.get_selection_bounds()
- if return_val:
- # Something has been selected, get the text
- start_sel, finish_sel = return_val[0], return_val[1]
- self._selected_text = buffer_.get_text(
- start_sel, finish_sel, True)
- elif iter_.get_char() and ord(iter_.get_char()) > 31:
- # Clicked on a word, take whole word for selection
- start_sel = iter_.copy()
- if not start_sel.starts_word():
- start_sel.backward_word_start()
- finish_sel = iter_.copy()
- if not finish_sel.ends_word():
- finish_sel.forward_word_end()
- self._selected_text = buffer_.get_text(
- start_sel, finish_sel, True)
- return False
-
- def _on_populate_popup(self,
- _textview: Gtk.TextView,
- menu: Gtk.Menu
- ) -> None:
- '''
- Overrides the default context menu.
- If text is selected, a submenu with actions on the selection is added.
- (see _on_button_press)
- '''
- if not self._selected_text:
- menu.show_all()
- return
-
- action_menu_item = get_conv_action_context_menu(
- self._account, self._selected_text)
- menu.prepend(action_menu_item)
- menu.show_all()
-
- def _on_uri_clicked(self,
- texttag: Gtk.TextTag,
- _widget: Any,
- event: Gdk.Event,
- iter_: Gtk.TextIter,
- _kind: Gtk.TextTag
- ) -> int:
- if event.type != Gdk.EventType.BUTTON_PRESS:
- return Gdk.EVENT_PROPAGATE
-
- begin_iter = iter_.copy()
- # we get the beginning of the tag
- while not begin_iter.starts_tag(texttag):
- begin_iter.backward_char()
- end_iter = iter_.copy()
- # we get the end of the tag
- while not end_iter.ends_tag(texttag):
- end_iter.forward_char()
-
- # Detect XHTML-IM link
- word = getattr(texttag, 'href', None)
- if not word:
- word = self.get_buffer().get_text(begin_iter, end_iter, True)
-
- uri = parse_uri(word)
- if event.button.button == 3: # right click
- self._show_uri_context_menu(uri)
- return Gdk.EVENT_STOP
-
- # TODO:
- # self.plugin_modified = False
- # app.plugin_manager.extension_point(
- # 'hyperlink_handler', uri, self, self.get_toplevel())
- # if self.plugin_modified:
- # return Gdk.EVENT_STOP
-
- open_uri(uri, account=self._account)
- return Gdk.EVENT_STOP
-
- def _show_uri_context_menu(self, uri: URI) -> None:
- menu = get_conv_uri_context_menu(self._account, uri)
- if menu is None:
- log.warning('No handler for URI type: %s', uri)
- return
-
- def _destroy(menu: Gtk.Menu, _pspec: GObject.ParamSpec) -> None:
- visible = menu.get_property('visible')
- if not visible:
- GLib.idle_add(menu.destroy)
-
- menu.attach_to_widget(self, None)
- menu.connect('notify::visible', _destroy)
- menu.popup_at_pointer()
=====================================
gajim/gtk/emoji_chooser.py deleted
=====================================
@@ -1,412 +0,0 @@
-# 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/>.
-
-import logging
-import weakref
-from collections import OrderedDict
-
-from gi.repository import Gtk
-from gi.repository import GLib
-from gi.repository import GdkPixbuf
-from gi.repository import Pango
-
-from gajim.common import app
-from gajim.common import helpers
-from gajim.common import configpaths
-
-from .builder import get_builder
-from .emoji_data import emoji_data
-from .emoji_data import emoji_pixbufs
-from .emoji_data import Emoji
-
-MODIFIER_MAX_CHILDREN_PER_LINE = 6
-MAX_CHILDREN_PER_LINE = 10
-
-log = logging.getLogger('gajim.emoji')
-
-
-class Section(Gtk.Box):
- def __init__(self, name, search_entry, press_cb, chooser):
- Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL)
- self._chooser = chooser
- self._press_cb = press_cb
- self.pixbuf_generator = None
- self.heading = Gtk.Label(label=name)
- self.heading.set_halign(Gtk.Align.START)
- self.heading.get_style_context().add_class('emoji-chooser-heading')
- self.add(self.heading)
-
- self.flowbox = Gtk.FlowBox()
- self.flowbox.get_style_context().add_class('emoji-chooser-flowbox')
- self.flowbox.set_max_children_per_line(MAX_CHILDREN_PER_LINE)
- self.flowbox.set_filter_func(self._filter_func, search_entry)
- self.flowbox.connect('child-activated', press_cb)
-
- self.add(self.flowbox)
- self.show_all()
-
- def _filter_func(self, child, search_entry):
- name = search_entry.get_text()
- if not name:
- self.show()
- return True
-
- if name in child.desc:
- self.show()
- return True
- return False
-
- def add_emoji(self, codepoint, attrs):
- # Return always True, this method is used for the emoji factory
- # called by GLib.idle_add()
- pixbuf = self._get_next_pixbuf()
-
- variations = attrs.get('variations', None)
- if variations is None:
- if pixbuf is None:
- return True
- self.flowbox.add(EmojiChild(codepoint, pixbuf, attrs['desc']))
- if pixbuf != 'font':
- # We save the pixbuf for fast access if we need to
- # replace a codepoint in the textview
- emoji_pixbufs[codepoint] = pixbuf
- else:
- if pixbuf is not None:
- chooser = ModifierChooser()
-
- # Iterate over the variations and add the codepoints
- for codepoint_ in variations.keys():
- pixbuf_ = self._get_next_pixbuf()
- if pixbuf_ is None:
- continue
-
- if pixbuf_ == 'font':
- if not self._chooser.font_supports_codepoint(
- codepoint_):
- continue
- else:
- emoji_pixbufs[codepoint_] = pixbuf_
-
- # Only codepoints are added which the
- # font or theme supports
- chooser.add_emoji(codepoint_, pixbuf_)
-
- # Check if we successfully added codepoints with modifiers
- if chooser.has_child:
- # If we have children then add a button
- # and set the popover
- child = EmojiModifierChild(
- codepoint, pixbuf, attrs['desc'])
- child.button.set_popover(chooser)
- chooser.flowbox.connect(
- 'child-activated', self._press_cb)
- else:
- # If no children were added, destroy the chooser
- # and add a EmojiChild instead of a EmojiModifierChild
- chooser.destroy()
- child = EmojiChild(codepoint, pixbuf, attrs['desc'])
-
- if pixbuf != 'font':
- emoji_pixbufs[codepoint] = pixbuf
-
- self.flowbox.add(child)
- else:
- # We don’t have a image for the base codepoint
- # so skip all modifiers of it
- for _entry in variations:
- pixbuf = self._get_next_pixbuf()
- return True
-
- def clear_emojis(self):
- def _remove_emoji(emoji):
- self.flowbox.remove(emoji)
- emoji.destroy()
- self.flowbox.foreach(_remove_emoji)
-
- def _get_next_pixbuf(self):
- if self.pixbuf_generator is None:
- return 'font'
- return next(self.pixbuf_generator, False)
-
-
-class EmojiChild(Gtk.FlowBoxChild):
- def __init__(self, codepoint, pixbuf, desc):
- Gtk.FlowBoxChild.__init__(self)
- self.desc = desc
- self.codepoint = codepoint
- self.pixbuf = pixbuf
- if pixbuf == 'font':
- self.add(Gtk.Label(label=codepoint))
- else:
- self.add(Gtk.Image.new_from_pixbuf(pixbuf))
- self.set_tooltip_text(desc)
- self.show_all()
-
- def get_emoji(self):
- if self.pixbuf != 'font':
- pixbuf = self.get_child().get_pixbuf()
- pixbuf = pixbuf.scale_simple(Emoji.INPUT_SIZE,
- Emoji.INPUT_SIZE,
- GdkPixbuf.InterpType.HYPER)
- return self.codepoint, pixbuf
- return self.codepoint, None
-
-
-class EmojiModifierChild(Gtk.FlowBoxChild):
- def __init__(self, codepoint, pixbuf, desc):
- Gtk.FlowBoxChild.__init__(self)
- self.desc = desc
- self.codepoint = codepoint
- self.pixbuf = pixbuf
-
- self.button = Gtk.MenuButton()
- self.button.set_relief(Gtk.ReliefStyle.NONE)
- self.button.connect('button-press-event', self._button_press)
-
- if pixbuf == 'font':
- self.button.remove(self.button.get_child())
- label = Gtk.Label(label=codepoint)
- self.button.add(label)
- else:
- self.button.get_child().set_from_pixbuf(pixbuf)
-
- self.set_tooltip_text(desc)
- self.add(self.button)
- self.show_all()
-
- def _button_press(self, button, event):
- if event.button == 3:
- button.get_popover().show()
- button.get_popover().get_child().unselect_all()
- return True
- if event.button == 1:
- self.get_parent().emit('child-activated', self)
- return True
- return False
-
- def get_emoji(self):
- if self.pixbuf != 'font':
- pixbuf = self.button.get_child().get_pixbuf()
- pixbuf = pixbuf.scale_simple(Emoji.INPUT_SIZE,
- Emoji.INPUT_SIZE,
- GdkPixbuf.InterpType.HYPER)
- return self.codepoint, pixbuf
- return self.codepoint, None
-
-
-class ModifierChooser(Gtk.Popover):
- def __init__(self):
- Gtk.Popover.__init__(self)
- self.set_name('EmoticonPopover')
- self._has_child = False
-
- self.flowbox = Gtk.FlowBox()
- self.flowbox.get_style_context().add_class(
- 'emoji-modifier-chooser-flowbox')
- self.flowbox.set_size_request(200, -1)
- self.flowbox.set_max_children_per_line(MODIFIER_MAX_CHILDREN_PER_LINE)
- self.flowbox.show()
- self.add(self.flowbox)
-
- @property
- def has_child(self):
- return self._has_child
-
- def add_emoji(self, codepoint, pixbuf):
- self.flowbox.add(EmojiChild(codepoint, pixbuf, None))
- self._has_child = True
-
-
-class EmojiChooser(Gtk.Popover):
-
- _section_names = [
- 'Smileys & People',
- 'Animals & Nature',
- 'Food & Drink',
- 'Travel & Places',
- 'Activities',
- 'Objects',
- 'Symbols',
- 'Flags'
- ]
-
- def __init__(self):
- super().__init__()
- self.set_name('EmoticonPopover')
- self._text_widget = None
- self._load_source_id = None
- self._pango_layout = Pango.Layout(self.get_pango_context())
-
- self._builder = get_builder('emoji_chooser.ui')
- self._search = self._builder.get_object('search')
- self._stack = self._builder.get_object('stack')
-
- self._sections = OrderedDict()
- for name in self._section_names:
- self._sections[name] = Section(
- name, self._search, self._on_emoticon_press, self)
-
- section_box = self._builder.get_object('section_box')
- for section in self._sections.values():
- section_box.add(section)
-
- self.add(self._builder.get_object('box'))
-
- self.connect('key-press-event', self._key_press)
- self._builder.connect_signals(self)
-
- @property
- def text_widget(self):
- return self._text_widget
-
- @text_widget.setter
- def text_widget(self, value):
- # Hold only a weak reference so if we can destroy
- # the ChatControl
- self._text_widget = weakref.ref(value)
-
- def _key_press(self, _widget, event):
- return self._search.handle_event(event)
-
- def _search_changed(self, _entry):
- for section in self._sections.values():
- section.hide()
- section.flowbox.invalidate_filter()
- self._switch_stack()
-
- def _clear_sections(self):
- for section in self._sections.values():
- section.clear_emojis()
-
- def _switch_stack(self):
- for section in self._sections.values():
- if section.is_visible():
- self._stack.set_visible_child_name('list')
- return
- self._stack.set_visible_child_name('not-found')
-
- @staticmethod
- def _get_current_theme():
- theme = app.settings.get('emoticons_theme')
- themes = helpers.get_available_emoticon_themes()
- if theme not in themes:
- app.settings.set('emoticons_theme', 'noto')
- theme = 'noto'
- return theme
-
- @staticmethod
- def _get_emoji_theme_path(theme):
- if theme == 'font':
- return 'font'
-
- base_path = configpaths.get('EMOTICONS')
- emoticons_data_path = base_path / theme / f'{theme}.png'
- if emoticons_data_path.exists():
- return emoticons_data_path
-
- emoticons_user_path = configpaths.get('MY_EMOTS') / f'{theme}.png'
- if emoticons_user_path.exists():
- return emoticons_user_path
-
- log.warning('Could not find emoji theme: %s', theme)
- return None
-
- def load(self):
- theme = self._get_current_theme()
- path = self._get_emoji_theme_path(theme)
- if not theme or path is None:
- self._clear_sections()
- emoji_pixbufs.clear()
- return
-
- # Attach pixbuf generator
- pixbuf_generator = None
- if theme != 'font':
- pixbuf_generator = self._get_next_pixbuf(path)
- for section in self._sections.values():
- section.pixbuf_generator = pixbuf_generator
-
- if self._load_source_id is not None:
- GLib.source_remove(self._load_source_id)
-
- # When we change emoji theme
- self._clear_sections()
- emoji_pixbufs.clear()
-
- factory = self._emoji_factory(theme == 'font')
- self._load_source_id = GLib.idle_add(lambda: next(factory, False),
- priority=GLib.PRIORITY_LOW)
-
- def _emoji_factory(self, font):
- for codepoint, attrs in emoji_data.items():
- if not attrs['fully-qualified']:
- # We don’t add these to the UI
- continue
-
- if font and not self.font_supports_codepoint(codepoint):
- continue
-
- section = self._sections[attrs['group']]
- yield section.add_emoji(codepoint, attrs)
- self._load_source_id = None
- emoji_pixbufs.complete = True
-
- def font_supports_codepoint(self, codepoint):
- self._pango_layout.set_text(codepoint, -1)
- if self._pango_layout.get_unknown_glyphs_count():
- return False
- if len(codepoint) > 1:
- # The font supports each of the codepoints
- # Check if the rendered glyph is more than one char
- if self._pango_layout.get_size()[0] > 19000:
- return False
- return True
-
- @staticmethod
- def _get_next_pixbuf(path):
- src_x = src_y = cur_column = 0
- atlas = GdkPixbuf.Pixbuf.new_from_file(str(path))
-
- while True:
- src_x = cur_column * Emoji.PARSE_WIDTH
- subpixbuf = atlas.new_subpixbuf(src_x, src_y,
- Emoji.PARSE_WIDTH,
- Emoji.PARSE_HEIGHT)
-
- if list(subpixbuf.get_pixels())[0:4] == [0, 0, 0, 255]:
- # top left corner is a black pixel means image is missing
- subpixbuf = None
-
- if cur_column == Emoji.PARSE_COLUMNS - 1:
- src_y += Emoji.PARSE_WIDTH
- cur_column = 0
- else:
- cur_column += 1
-
- yield subpixbuf
-
- def _on_emoticon_press(self, flowbox, child):
- GLib.timeout_add(100, flowbox.unselect_child, child)
- codepoint, pixbuf = child.get_emoji()
- self._text_widget().insert_emoji(codepoint, pixbuf)
- self._text_widget().grab_focus()
-
- def do_destroy(self):
- # Remove the references we hold to other objects
- self._text_widget = None
- # Never destroy, creating a new EmoticonPopover is expensive
- return True
-
-
-emoji_chooser = EmojiChooser()
=====================================
gajim/gtk/emoji_data.py deleted
=====================================
The diff for this file was not included because it is too large.
=====================================
gajim/gtk/emoji_data.pyi deleted
=====================================
@@ -1,44 +0,0 @@
-# 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 enum import IntEnum
-from collections import OrderedDict
-
-from gi.repository import GdkPixbuf
-from gi.repository import Gtk
-
-class Emoji(IntEnum):
- ...
-
-def is_emoji(codepoints: str) -> bool: ...
-def get_emoji_pixbuf(codepoints: str) -> GdkPixbuf.Pixbuf: ...
-
-class EmojiData(OrderedDict):
- def get_regex(self) -> str: ...
-
-class EmojiPixbufs(dict):
- @property
- def complete(self) -> bool: ...
- @complete.setter
- def complete(self, value: bool) -> None: ...
- def clear(self) -> None: ...
- def append_marks(self,
- textview: Gtk.TextView,
- start: Gtk.TextMark,
- end: Gtk.TextMark,
- codepoint: str) -> None: ...
-
-emoji_pixbufs: EmojiPixbufs
-emoji_ascii_data: dict[str, str]
-emoji_data: EmojiData
=====================================
gajim/gtk/message_actions_box.py
=====================================
@@ -18,7 +18,6 @@
from typing import Optional
import os
-import sys
import tempfile
import logging
import uuid
@@ -45,7 +44,6 @@
from .builder import get_builder
from .dialogs import ErrorDialog
-from .emoji_chooser import emoji_chooser
from .menus import get_encryption_menu
from .menus import get_format_menu
from .menus import get_groupchat_menu
@@ -98,8 +96,6 @@ def __init__(self) -> None:
self._ui.quick_invite_button.set_action_name('win.muc-invite')
- self._init_emoticon_popover()
-
self.show_all()
self._ui.connect_signals(self)
@@ -157,7 +153,7 @@ def _on_action(self,
self._on_format(action_name)
elif action_name == 'show-emoji-chooser':
- self._on_show_emoji_chooser()
+ self.msg_textview.emit('insert-emoji')
elif action_name == 'quote':
assert param
@@ -389,31 +385,6 @@ def _set_settings_menu(self, contact: ChatContactT) -> None:
menu = get_singlechat_menu(contact)
self._ui.settings_menu.set_menu_model(menu)
- def _init_emoticon_popover(self) -> None:
- if not app.settings.get('emoticons_theme'):
- return
-
- if sys.platform == 'darwin':
- emoji_chooser.text_widget = self.msg_textview
- self._ui.emoticons_button.set_popover(emoji_chooser)
- return
-
- self._ui.emoticons_button.set_sensitive(True)
- self._ui.emoticons_button.connect('clicked',
- self._on_emoticon_button_clicked)
-
- def toggle_emoticons(self) -> None:
- if app.settings.get('emoticons_theme'):
- self._ui.emoticons_button.set_no_show_all(False)
- self._ui.emoticons_button.show()
- else:
- self._ui.emoticons_button.set_no_show_all(True)
- self._ui.emoticons_button.hide()
-
- def _on_emoticon_button_clicked(self, _widget: Gtk.Button) -> None:
- self.msg_textview.emit('insert-emoji')
- self._ui.emoticons_button.set_active(False)
-
def _on_format(self, name: str) -> None:
name = name.removeprefix('input-')
self.msg_textview.apply_formatting(name)
@@ -421,14 +392,6 @@ def _on_format(self, name: str) -> None:
def _on_clear(self) -> None:
self.msg_textview.clear()
- def _on_show_emoji_chooser(self) -> None:
- if sys.platform == 'darwin':
- popover = self._ui.emoticons_button.get_popover()
- assert popover
- popover.show()
- else:
- self.msg_textview.emit('insert-emoji')
-
def _on_send_file_enabled_changed(self,
action: Gio.SimpleAction,
_param: GObject.ParamSpec) -> None:
=====================================
gajim/gtk/message_input.py
=====================================
@@ -25,11 +25,9 @@
from collections import defaultdict
import logging
-import sys
from gi.repository import Gtk
from gi.repository import Gdk
-from gi.repository import GdkPixbuf
from gi.repository import GLib
from gi.repository import Pango
@@ -420,60 +418,6 @@ def apply_formatting(self, formatting: str) -> None:
buf.get_iter_at_offset(start_offset),
buf.get_iter_at_offset(end_offset + 2))
- def replace_emojis(self) -> None:
- if sys.platform != 'darwin':
- # TODO: Remove if colored emoji rendering works well on MacOS
- return
-
- theme = app.settings.get('emoticons_theme')
- if not theme or theme == 'font':
- return
-
- def _replace(anchor: Gtk.TextChildAnchor) -> None:
- if anchor is None:
- return
- image = anchor.get_widgets()[0]
- if hasattr(image, 'codepoint'):
- # found emoji
- codepoint = getattr(image, 'codepoint')
- self._replace_char_at_iter(iter_, codepoint)
- image.destroy()
-
- iter_ = self.get_buffer().get_start_iter()
- _replace(iter_.get_child_anchor())
-
- while iter_.forward_char():
- _replace(iter_.get_child_anchor())
-
- def _replace_char_at_iter(self,
- iter_: Gtk.TextIter,
- new_char: str
- ) -> None:
- buf = self.get_buffer()
- iter_2 = iter_.copy()
- iter_2.forward_char()
- buf.delete(iter_, iter_2)
- buf.insert(iter_, new_char)
-
- def insert_emoji(self, codepoint: str, pixbuf: GdkPixbuf.Pixbuf) -> None:
- buf = self.get_buffer()
- if buf.get_char_count():
- # buffer contains text
- buf.insert_at_cursor(' ')
-
- insert_mark = buf.get_insert()
- insert_iter = buf.get_iter_at_mark(insert_mark)
-
- if pixbuf is None:
- buf.insert(insert_iter, codepoint)
- else:
- anchor = buf.create_child_anchor(insert_iter)
- image = Gtk.Image.new_from_pixbuf(pixbuf)
- setattr(image, 'codepoint', codepoint)
- image.show()
- self.add_child_at_anchor(image, anchor)
- buf.insert_at_cursor(' ')
-
def clear(self, *args: Any) -> None:
buf = self.get_buffer()
start, end = buf.get_bounds()
=====================================
gajim/gtk/preferences.py
=====================================
@@ -26,7 +26,6 @@
from gajim.common import app
from gajim.common import configpaths
-from gajim.common import helpers
from gajim.common.const import THRESHOLD_OPTIONS
from gajim.common.i18n import _
from gajim.common.helpers import open_file
@@ -42,7 +41,6 @@
from .const import SettingType
from .dialogs import DialogButton
from .dialogs import ConfirmationDialog
-from .emoji_chooser import emoji_chooser
from .settings import PopoverSetting
from .settings import SettingsBox
from .settings import SettingsDialog
@@ -81,8 +79,6 @@ def __init__(self) -> None:
self.add(self._ui.grid)
- self._check_emoji_theme()
-
prefs: list[tuple[str, Type[PreferenceBox]]] = [
('window_behaviour', WindowBehaviour),
('plugins', Plugins),
@@ -95,7 +91,6 @@ def __init__(self) -> None:
('status_message', StatusMessage),
('automatic_status', AutomaticStatus),
('themes', Themes),
- ('emoji', Emoji),
('server', Server),
('audio', Audio),
('video', Video),
@@ -117,10 +112,6 @@ def __init__(self) -> None:
self._ui.connect_signals(self)
self.show_all()
- if sys.platform != 'darwin':
- # TODO: Remove if colored emoji rendering works well on
- # Windows and MacOS
- self._ui.emoji.hide()
def get_ui(self):
return self._ui
@@ -157,14 +148,6 @@ def update_proxy_list(self) -> None:
miscellaneous = cast(Miscellaneous, self._prefs['miscellaneous'])
miscellaneous.update_proxy_list()
- @staticmethod
- def _check_emoji_theme() -> None:
- # Ensure selected emoji theme is valid
- emoji_themes = helpers.get_available_emoticon_themes()
- settings_theme = app.settings.get('emoticons_theme')
- if settings_theme not in emoji_themes:
- app.settings.set('emoticons_theme', 'font')
-
def _on_destroy(self, _widget: Gtk.Widget) -> None:
self._prefs.clear()
app.check_finalize(self)
@@ -704,42 +687,6 @@ def _on_dark_theme(value: str, *args: Any) -> None:
app.ged.raise_event(StyleChanged())
-class Emoji(PreferenceBox):
- def __init__(self, *args: Any) -> None:
- if sys.platform != 'darwin':
- # TODO: Remove if colored emoji rendering works well on
- # Windows and MacOS
- PreferenceBox.__init__(self, [])
- return
-
- emoji_themes_items: list[str] = []
- for theme in helpers.get_available_emoticon_themes():
- emoji_themes_items.append(theme)
-
- settings = [
- Setting(SettingKind.POPOVER,
- _('Emoji Theme'),
- SettingType.CONFIG,
- 'emoticons_theme',
- desc=_('Choose from various emoji styles'),
- props={'entries': emoji_themes_items},
- callback=self._on_emoticons_theme)
- ]
-
- PreferenceBox.__init__(self, settings)
-
- def _on_emoticons_theme(self, *args: Any) -> None:
- emoji_chooser.load()
- self._toggle_emoticons()
-
- @staticmethod
- def _toggle_emoticons() -> None:
- # TODO
- # for ctrl in app.window.get_controls():
- # ctrl.toggle_emoticons()
- pass
-
-
class Server(PreferenceBox):
def __init__(self, *args: Any) -> None:
=====================================
pyproject.toml
=====================================
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
[tool.codespell]
-skip = "*__pycache__*,gajim/gui,gajim/data/icons,gajim/data/sounds,gajim/data/emoticons"
+skip = "*__pycache__*,gajim/gui,gajim/data/icons,gajim/data/sounds"
ignore-words-list = "claus,pres,ser,trough"
=====================================
scripts/get_iana_data.py
=====================================
@@ -0,0 +1,74 @@
+#!/usr/bin/env python3
+
+import argparse
+from datetime import datetime
+from urllib.request import urlopen
+import csv
+import io
+import logging
+from pathlib import Path
+import re
+import sys
+
+
+SCHEMES_URL = 'https://www.iana.org/assignments/uri-schemes/uri-schemes-1.csv'
+
+logging.basicConfig(level='INFO', format='%(levelname)s: %(message)s')
+
+
+def download_file(url: str) -> str:
+ logging.info('Download: %s', url)
+ with urlopen(url) as f:
+ content = f.read()
+ return content.decode()
+
+
+def parse_uri_schemes(content: str) -> list[str]:
+ logging.info('Parse uri schemes')
+ schemes: list[str] = []
+
+ reader = csv.reader(io.StringIO(content), delimiter=',')
+ next(reader) # Skip header row
+ for line in reader:
+ scheme = line[0].lower().removesuffix(' (obsolete)')
+ if not re.fullmatch('[a-z0-9+.-]+', scheme):
+ logging.warning('unexpected scheme field contents: %s', scheme)
+ continue
+
+ schemes.append(scheme)
+
+ return schemes
+
+
+def generate_output(schemes: list[str],
+ out_path: Path):
+
+ logging.info('Generate output')
+ current_date = datetime.utcnow().isoformat()
+ content = f'# Generated by get_iana_data.py @ {current_date}\n'
+
+ content += '\n'
+
+ content += 'URI_SCHEMES = {\n'
+ for scheme in schemes:
+ content += f" '{scheme}',\n"
+ content += '}\n'
+
+ outpath.write_text(content)
+ logging.info('Wrote file to %s', outpath)
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description='Generate IANA data')
+ parser.add_argument('out', help='Path to output file')
+ args = parser.parse_args()
+
+ outpath = Path(args.out)
+ if outpath.is_dir():
+ sys.exit('Output path is a directory')
+
+ scheme_content = download_file(SCHEMES_URL)
+ schemes = parse_uri_schemes(scheme_content)
+ output = generate_output(schemes,
+ outpath)
+ logging.info('Finished !')
=====================================
setup.cfg
=====================================
@@ -49,8 +49,6 @@ install_requires =
[options.package_data]
gajim =
- data/emoticons/*/*.png
- data/emoticons/*/LICENSE
data/gui/*.ui
data/icons/hicolor/*/*/*.png
data/icons/hicolor/*/*/*.svg
=====================================
test/no_gui/test_styling.py
=====================================
@@ -1,4 +1,5 @@
import unittest
+from unittest.mock import MagicMock
from gajim import gui
gui.init('gtk')
@@ -17,6 +18,10 @@
from gajim.common.text_helpers import jid_to_iri
+app.settings = MagicMock()
+app.settings.get.return_value = 'a a- a. scheme' # additional_uri_schemes
+
+
STYLING = {
'pre cannot have children': {
'input': '_no pre `with *children*`_',
@@ -384,6 +389,14 @@
' ',
'.',
':',
+ '://',
+ 'Böblingen:🌥',
+ 'Bo\u0308blingen:🌥', # finds blingen:🌥 but rejects an unregistered scheme
+ 'path/to/file_na.me:123',
+ '_sche.me:body_',
+ '_sche+me:body_',
+ '.scheme:body',
+ '+scheme:body',
# These are from https://mathiasbynens.be/demo/url-regex
'//',
@@ -391,10 +404,6 @@
'///a',
'///',
'foo.com',
- 'http://foo.bar?q=Spaces should be encoded',
- 'http:// shouldfail.com',
- ':// should fail',
- 'http://foo.bar/foo(bar)baz quux',
]
@@ -427,6 +436,8 @@
'file:a',
'file:a/',
'file:a/b',
+
+ 'mailtomailto:foo@bar.com.uk',
]
@@ -484,7 +495,6 @@
# These are from https://rfc-editor.org/rfc/rfc7622#section-3.5
'"juliet"@example.com',
- 'foo bar(a)example.com', # search is expected to find 'bar(a)example.com'
'@example.com',
'henryⅣ(a)example.com', # localpart has a compatibility-decomposable cp
'♚(a)example.com', # localpart has a symbol cp
@@ -495,13 +505,14 @@
URIS_WITH_TEXT = [
('write to my email mailto:foo@bar.com.uk (but not to mailto:bar@foo.com)',
['mailto:foo@bar.com.uk', 'mailto:bar@foo.com']),
- ('write to my email mailtomailto:foo@bar.com.uk (but not to mailto:bar@foo.com)',
- ['mailtomailto:foo@bar.com.uk', 'mailto:bar@foo.com']),
('see this http://userid@example.com/ link', ['http://userid@example.com/']),
('see this http://userid@example.com/, and ..', ['http://userid@example.com/']),
('<http://userid@example.com/>', ['http://userid@example.com/']),
('"http://userid@example.com/"', ['http://userid@example.com/']),
('regexes are useless (see https://en.wikipedia.org/wiki/Recursion_(computer_science)), but comfy', ['https://en.wikipedia.org/wiki/Recursion_(computer_science)']),
+ ('(scheme:body)', ['scheme:body']),
+ ('/scheme:body', ['scheme:body']),
+ ('!scheme:body', ['scheme:body']),
]
@@ -519,41 +530,32 @@ def test_uris(self):
for uri in URIS:
text = self.wrap(uri)
hlinks = process_uris(text)
- self.assertEqual(len(hlinks), 1, text)
- self.assertEqual(hlinks[0].uri, uri, text)
+ self.assertEqual([l.uri for l in hlinks], [uri], text)
def test_invalid_uris(self):
for foo in NONURIS + UNACCEPTABLE_URIS:
text = self.wrap(foo)
hlinks = process_uris(text)
- if len(hlinks) == 0:
- continue
- self.assertEqual(len(hlinks), 1, text)
- self.assertNotEqual(hlinks[0].text, foo, text)
+ self.assertEqual([l.text for l in hlinks], [], text)
def test_jids(self):
for jid in JIDS:
text = self.wrap(jid)
+ uri = jid_to_iri(jid)
hlinks = process_uris(text)
- self.assertEqual(len(hlinks), 1, text)
- self.assertEqual(hlinks[0].text, jid, text)
- self.assertEqual(hlinks[0].uri, jid_to_iri(jid), text)
+ self.assertEqual([(l.text, l.uri) for l in hlinks],
+ [(jid, uri)], text)
def test_nonjids(self):
for foo in NONJIDS:
text = self.wrap(foo)
hlinks = process_uris(text)
- if len(hlinks) == 0:
- continue
- self.assertEqual(len(hlinks), 1, text)
- self.assertNotEqual(hlinks[0].text, foo, text)
+ self.assertEqual([l.text for l in hlinks], [], text)
def test_uris_with_text(self):
- for text, result in URIS_WITH_TEXT:
+ for text, results in URIS_WITH_TEXT:
hlinks = process_uris(text)
- self.assertEqual(len(hlinks), len(result), text)
- for i, res in enumerate(result):
- self.assertEqual(hlinks[i].text, res, text)
+ self.assertEqual([l.text for l in hlinks], results, text)
if __name__ == "__main__":
View it on GitLab: https://dev.gajim.org/gajim/gajim/-/compare/2434c6a591f8db97193bcd1aa416dac…
--
View it on GitLab: https://dev.gajim.org/gajim/gajim/-/compare/2434c6a591f8db97193bcd1aa416dac…
You're receiving this email because of your account on dev.gajim.org.
Daniel Brötzmann pushed to branch remove-old-emoji-code at gajim / gajim
Commits:
2f8986b3 by wurstsalat at 2022-11-30T00:22:34+01:00
cq: Remove unnecessary pyright ignore comments
- - - - -
2434c6a5 by wurstsalat at 2022-11-30T00:22:55+01:00
refactor: Remove old emoji code
Fixes #10932
- - - - -
24 changed files:
- gajim/common/configpaths.py
- gajim/common/dbus/remote_control.py
- gajim/common/helpers.py
- gajim/common/setting_values.py
- gajim/common/settings.py
- gajim/common/styling.py
- − gajim/data/emoticons/noto/LICENSE
- − gajim/data/emoticons/noto/noto.png
- − gajim/data/emoticons/twemoji/LICENSE
- − gajim/data/emoticons/twemoji/twemoji.png
- gajim/data/gui/message_actions_box.ui
- gajim/data/gui/preferences.ui
- gajim/data/style/gajim.css
- gajim/gtk/application.py
- gajim/gtk/chat_stack.py
- gajim/gtk/conversation/plain_widget.py
- − gajim/gtk/emoji_chooser.py
- − gajim/gtk/emoji_data.py
- − gajim/gtk/emoji_data.pyi
- gajim/gtk/message_actions_box.py
- gajim/gtk/message_input.py
- gajim/gtk/preferences.py
- pyproject.toml
- setup.cfg
Changes:
=====================================
gajim/common/configpaths.py
=====================================
@@ -125,7 +125,6 @@ def __init__(self) -> None:
source_paths = [
('DATA', basedir / 'data'),
('STYLE', basedir / 'data' / 'style'),
- ('EMOTICONS', basedir / 'data' / 'emoticons'),
('GUI', basedir / 'data' / 'gui'),
('ICONS', basedir / 'data' / 'icons'),
('HOME', Path.home()),
@@ -216,8 +215,6 @@ def init(self):
('PLUGINS_IMAGES', 'plugins_images',
PathLocation.CACHE, PathType.FOLDER),
('PLUGINS_USER', 'plugins', PathLocation.DATA, PathType.FOLDER),
- ('MY_EMOTS',
- 'emoticons', PathLocation.DATA, PathType.FOLDER_OPTIONAL),
('MY_ICONSETS',
'iconsets', PathLocation.DATA, PathType.FOLDER_OPTIONAL),
=====================================
gajim/common/dbus/remote_control.py
=====================================
@@ -314,7 +314,7 @@ def change_status(status: str, message: str, account: str) -> bool:
GLib.idle_add(app.get_client(account).change_status,
status,
- message) # pyright: ignore
+ message)
else:
# account not specified, so change the status of all accounts
for acc in app.settings.get_active_accounts():
@@ -327,7 +327,7 @@ def change_status(status: str, message: str, account: str) -> bool:
GLib.idle_add(app.get_client(acc).change_status,
status,
- message) # pyright: ignore
+ message)
return True
@staticmethod
=====================================
gajim/common/helpers.py
=====================================
@@ -765,22 +765,6 @@ def version_condition(current_version: str, required_version: str) -> bool:
return True
-def get_available_emoticon_themes() -> list[str]:
- files: list[Path] = []
- for folder in configpaths.get('EMOTICONS').iterdir():
- if not folder.is_dir():
- continue
- files += [theme for theme in folder.iterdir() if theme.is_file()]
-
- my_emots = configpaths.get('MY_EMOTS')
- if my_emots.is_dir():
- files += list(my_emots.iterdir())
-
- emoticons_themes = ['font']
- emoticons_themes += [file.stem for file in files if file.suffix == '.png']
- return sorted(emoticons_themes)
-
-
def call_counter(func):
def helper(self, restart=False):
if restart:
=====================================
gajim/common/setting_values.py
=====================================
@@ -48,7 +48,6 @@ class _ACCOUNT_DEFAULT:
'confirm_close_muc',
'confirm_on_window_delete',
'dev_force_bookmark_2',
- 'dev_use_message_label',
'developer_modus',
'enable_emoji_shortcodes',
'enable_negative_priority',
@@ -136,7 +135,6 @@ class _ACCOUNT_DEFAULT:
'date_format',
'date_time_format',
'dictionary_url',
- 'emoticons_theme',
'gc_refer_to_nick_char',
'global_proxy',
'last_save_dir',
@@ -192,7 +190,6 @@ class _ACCOUNT_DEFAULT:
'dev_use_message_label': True,
'developer_modus': False,
'dictionary_url': 'WIKTIONARY',
- 'emoticons_theme': 'noto-emoticons',
'enable_emoji_shortcodes': True,
'enable_negative_priority': False,
'enable_file_preview': True,
=====================================
gajim/common/settings.py
=====================================
@@ -237,11 +237,6 @@ def _setup_installation_defaults() -> None:
if IS_PORTABLE:
APP_SETTINGS['use_keyring'] = False
- if sys.platform in ('win32', 'darwin'):
- # Cairo does not support color emojis fonts on
- # Windows and MacOS
- APP_SETTINGS['dev_use_message_label'] = False
-
def _load_app_overrides(self) -> None:
if not OVERRIDES_PATH.exists():
return
=====================================
gajim/common/styling.py
=====================================
@@ -19,7 +19,6 @@
from typing import Optional
import string
-import sys
import re
from dataclasses import dataclass
from dataclasses import field
@@ -31,7 +30,6 @@
from gajim.common.helpers import parse_uri as analyze_uri
from gajim.common.helpers import validate_jid
from gajim.common.text_helpers import jid_to_iri
-from gajim.gui.emoji_data import emoji_data
PRE = '`'
STRONG = '*'
@@ -52,9 +50,6 @@
URI_OR_JID_RX = re.compile(fr'(?P<uri>{regex.IRI})|(?P<jid>{regex.XMPP.jid})')
-EMOJI_RX = emoji_data.get_regex()
-EMOJI_RX = re.compile(EMOJI_RX)
-
SD = 0
SD_POS = 1
MAX_QUOTE_LEVEL = 20
@@ -96,11 +91,6 @@ class MailAddress(BaseHyperlink):
name: str = field(default='mailadr', init=False)
-@dataclass
-class Emoji(StyleObject):
- name: str = field(default='emoji', init=False)
-
-
@dataclass
class Block(StyleObject):
@@ -116,7 +106,6 @@ class PlainBlock(Block):
name: str = field(default='plain', init=False)
spans: list[Span] = field(default_factory=list)
uris: list[BaseHyperlink] = field(default_factory=list)
- emojis: list[Emoji] = field(default_factory=list)
@classmethod
def from_quote_match(cls, match: Match[str]) -> PlainBlock:
@@ -207,10 +196,6 @@ def process(text: Union[str, bytes], level: int = 0) -> ParsingResult:
for line in block.text.splitlines(keepends=True):
block.spans += _parse_line(line, offset, offset_bytes)
block.uris += _parse_uris(line, offset, offset_bytes)
- if sys.platform == 'darwin':
- # block.emojis is used for replacing emojis with Gtk.Images
- # Necessary for MessageTextview (darwin) only
- block.emojis += _parse_emojis(line, offset)
offset += len(line)
offset_bytes += len(line.encode())
@@ -360,22 +345,6 @@ def make(start: int, end: int, is_jid: bool) -> Optional[BaseHyperlink]:
return uris
-def _parse_emojis(line: str, offset: int) -> list[Emoji]:
- emojis: list[Emoji] = []
-
- def make(match: Match[str]) -> Emoji:
- return _make_emoji(line,
- match.start(),
- match.end() - 1,
- offset)
-
- for match in EMOJI_RX.finditer(line):
- emoji = make(match)
- emojis.append(emoji)
-
- return emojis
-
-
def _handle_pre_span(line: str,
index: int,
offset: int,
@@ -463,18 +432,6 @@ def _make_hyperlink(line: str,
text=text)
-def _make_emoji(line: str,
- start: int,
- end: int,
- offset: int) -> Emoji:
- text = line[start:end + 1]
- start += offset
- end += offset + 1
- return Emoji(start=start,
- end=end,
- text=text)
-
-
def _is_span_empty(sd: str, index: int, stack: list[tuple[str, int]]) -> bool:
start_sd = stack[-1][SD]
if start_sd != sd:
=====================================
gajim/data/emoticons/noto/LICENSE deleted
=====================================
@@ -1,201 +0,0 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
=====================================
gajim/data/emoticons/noto/noto.png deleted
=====================================
Binary files a/gajim/data/emoticons/noto/noto.png and /dev/null differ
=====================================
gajim/data/emoticons/twemoji/LICENSE deleted
=====================================
@@ -1,393 +0,0 @@
-Attribution 4.0 International
-
-=======================================================================
-
-Creative Commons Corporation ("Creative Commons") is not a law firm and
-does not provide legal services or legal advice. Distribution of
-Creative Commons public licenses does not create a lawyer-client or
-other relationship. Creative Commons makes its licenses and related
-information available on an "as-is" basis. Creative Commons gives no
-warranties regarding its licenses, any material licensed under their
-terms and conditions, or any related information. Creative Commons
-disclaims all liability for damages resulting from their use to the
-fullest extent possible.
-
-Using Creative Commons Public Licenses
-
-Creative Commons public licenses provide a standard set of terms and
-conditions that creators and other rights holders may use to share
-original works of authorship and other material subject to copyright
-and certain other rights specified in the public license below. The
-following considerations are for informational purposes only, are not
-exhaustive, and do not form part of our licenses.
-
- Considerations for licensors: Our public licenses are
- intended for use by those authorized to give the public
- permission to use material in ways otherwise restricted by
- copyright and certain other rights. Our licenses are
- irrevocable. Licensors should read and understand the terms
- and conditions of the license they choose before applying it.
- Licensors should also secure all rights necessary before
- applying our licenses so that the public can reuse the
- material as expected. Licensors should clearly mark any
- material not subject to the license. This includes other CC-
- licensed material, or material used under an exception or
- limitation to copyright. More considerations for licensors:
- wiki.creativecommons.org/Considerations_for_licensors
-
- Considerations for the public: By using one of our public
- licenses, a licensor grants the public permission to use the
- licensed material under specified terms and conditions. If
- the licensor's permission is not necessary for any reason--for
- example, because of any applicable exception or limitation to
- copyright--then that use is not regulated by the license. Our
- licenses grant only permissions under copyright and certain
- other rights that a licensor has authority to grant. Use of
- the licensed material may still be restricted for other
- reasons, including because others have copyright or other
- rights in the material. A licensor may make special requests,
- such as asking that all changes be marked or described.
- Although not required by our licenses, you are encouraged to
- respect those requests where reasonable. More_considerations
- for the public:
- wiki.creativecommons.org/Considerations_for_licensees
-
-=======================================================================
-
-Creative Commons Attribution 4.0 International Public License
-
-By exercising the Licensed Rights (defined below), You accept and agree
-to be bound by the terms and conditions of this Creative Commons
-Attribution 4.0 International Public License ("Public License"). To the
-extent this Public License may be interpreted as a contract, You are
-granted the Licensed Rights in consideration of Your acceptance of
-these terms and conditions, and the Licensor grants You such rights in
-consideration of benefits the Licensor receives from making the
-Licensed Material available under these terms and conditions.
-
-
-Section 1 -- Definitions.
-
- a. Adapted Material means material subject to Copyright and Similar
- Rights that is derived from or based upon the Licensed Material
- and in which the Licensed Material is translated, altered,
- arranged, transformed, or otherwise modified in a manner requiring
- permission under the Copyright and Similar Rights held by the
- Licensor. For purposes of this Public License, where the Licensed
- Material is a musical work, performance, or sound recording,
- Adapted Material is always produced where the Licensed Material is
- synched in timed relation with a moving image.
-
- b. Adapter's License means the license You apply to Your Copyright
- and Similar Rights in Your contributions to Adapted Material in
- accordance with the terms and conditions of this Public License.
-
- c. Copyright and Similar Rights means copyright and/or similar rights
- closely related to copyright including, without limitation,
- performance, broadcast, sound recording, and Sui Generis Database
- Rights, without regard to how the rights are labeled or
- categorized. For purposes of this Public License, the rights
- specified in Section 2(b)(1)-(2) are not Copyright and Similar
- Rights.
-
- d. Effective Technological Measures means those measures that, in the
- absence of proper authority, may not be circumvented under laws
- fulfilling obligations under Article 11 of the WIPO Copyright
- Treaty adopted on December 20, 1996, and/or similar international
- agreements.
-
- e. Exceptions and Limitations means fair use, fair dealing, and/or
- any other exception or limitation to Copyright and Similar Rights
- that applies to Your use of the Licensed Material.
-
- f. Licensed Material means the artistic or literary work, database,
- or other material to which the Licensor applied this Public
- License.
-
- g. Licensed Rights means the rights granted to You subject to the
- terms and conditions of this Public License, which are limited to
- all Copyright and Similar Rights that apply to Your use of the
- Licensed Material and that the Licensor has authority to license.
-
- h. Licensor means the individual(s) or entity(ies) granting rights
- under this Public License.
-
- i. Share means to provide material to the public by any means or
- process that requires permission under the Licensed Rights, such
- as reproduction, public display, public performance, distribution,
- dissemination, communication, or importation, and to make material
- available to the public including in ways that members of the
- public may access the material from a place and at a time
- individually chosen by them.
-
- j. Sui Generis Database Rights means rights other than copyright
- resulting from Directive 96/9/EC of the European Parliament and of
- the Council of 11 March 1996 on the legal protection of databases,
- as amended and/or succeeded, as well as other essentially
- equivalent rights anywhere in the world.
-
- k. You means the individual or entity exercising the Licensed Rights
- under this Public License. Your has a corresponding meaning.
-
-
-Section 2 -- Scope.
-
- a. License grant.
-
- 1. Subject to the terms and conditions of this Public License,
- the Licensor hereby grants You a worldwide, royalty-free,
- non-sublicensable, non-exclusive, irrevocable license to
- exercise the Licensed Rights in the Licensed Material to:
-
- a. reproduce and Share the Licensed Material, in whole or
- in part; and
-
- b. produce, reproduce, and Share Adapted Material.
-
- 2. Exceptions and Limitations. For the avoidance of doubt, where
- Exceptions and Limitations apply to Your use, this Public
- License does not apply, and You do not need to comply with
- its terms and conditions.
-
- 3. Term. The term of this Public License is specified in Section
- 6(a).
-
- 4. Media and formats; technical modifications allowed. The
- Licensor authorizes You to exercise the Licensed Rights in
- all media and formats whether now known or hereafter created,
- and to make technical modifications necessary to do so. The
- Licensor waives and/or agrees not to assert any right or
- authority to forbid You from making technical modifications
- necessary to exercise the Licensed Rights, including
- technical modifications necessary to circumvent Effective
- Technological Measures. For purposes of this Public License,
- simply making modifications authorized by this Section 2(a)
- (4) never produces Adapted Material.
-
- 5. Downstream recipients.
-
- a. Offer from the Licensor -- Licensed Material. Every
- recipient of the Licensed Material automatically
- receives an offer from the Licensor to exercise the
- Licensed Rights under the terms and conditions of this
- Public License.
-
- b. No downstream restrictions. You may not offer or impose
- any additional or different terms or conditions on, or
- apply any Effective Technological Measures to, the
- Licensed Material if doing so restricts exercise of the
- Licensed Rights by any recipient of the Licensed
- Material.
-
- 6. No endorsement. Nothing in this Public License constitutes or
- may be construed as permission to assert or imply that You
- are, or that Your use of the Licensed Material is, connected
- with, or sponsored, endorsed, or granted official status by,
- the Licensor or others designated to receive attribution as
- provided in Section 3(a)(1)(A)(i).
-
- b. Other rights.
-
- 1. Moral rights, such as the right of integrity, are not
- licensed under this Public License, nor are publicity,
- privacy, and/or other similar personality rights; however, to
- the extent possible, the Licensor waives and/or agrees not to
- assert any such rights held by the Licensor to the limited
- extent necessary to allow You to exercise the Licensed
- Rights, but not otherwise.
-
- 2. Patent and trademark rights are not licensed under this
- Public License.
-
- 3. To the extent possible, the Licensor waives any right to
- collect royalties from You for the exercise of the Licensed
- Rights, whether directly or through a collecting society
- under any voluntary or waivable statutory or compulsory
- licensing scheme. In all other cases the Licensor expressly
- reserves any right to collect such royalties.
-
-
-Section 3 -- License Conditions.
-
-Your exercise of the Licensed Rights is expressly made subject to the
-following conditions.
-
- a. Attribution.
-
- 1. If You Share the Licensed Material (including in modified
- form), You must:
-
- a. retain the following if it is supplied by the Licensor
- with the Licensed Material:
-
- i. identification of the creator(s) of the Licensed
- Material and any others designated to receive
- attribution, in any reasonable manner requested by
- the Licensor (including by pseudonym if
- designated);
-
- ii. a copyright notice;
-
- iii. a notice that refers to this Public License;
-
- iv. a notice that refers to the disclaimer of
- warranties;
-
- v. a URI or hyperlink to the Licensed Material to the
- extent reasonably practicable;
-
- b. indicate if You modified the Licensed Material and
- retain an indication of any previous modifications; and
-
- c. indicate the Licensed Material is licensed under this
- Public License, and include the text of, or the URI or
- hyperlink to, this Public License.
-
- 2. You may satisfy the conditions in Section 3(a)(1) in any
- reasonable manner based on the medium, means, and context in
- which You Share the Licensed Material. For example, it may be
- reasonable to satisfy the conditions by providing a URI or
- hyperlink to a resource that includes the required
- information.
-
- 3. If requested by the Licensor, You must remove any of the
- information required by Section 3(a)(1)(A) to the extent
- reasonably practicable.
-
- 4. If You Share Adapted Material You produce, the Adapter's
- License You apply must not prevent recipients of the Adapted
- Material from complying with this Public License.
-
-
-Section 4 -- Sui Generis Database Rights.
-
-Where the Licensed Rights include Sui Generis Database Rights that
-apply to Your use of the Licensed Material:
-
- a. for the avoidance of doubt, Section 2(a)(1) grants You the right
- to extract, reuse, reproduce, and Share all or a substantial
- portion of the contents of the database;
-
- b. if You include all or a substantial portion of the database
- contents in a database in which You have Sui Generis Database
- Rights, then the database in which You have Sui Generis Database
- Rights (but not its individual contents) is Adapted Material; and
-
- c. You must comply with the conditions in Section 3(a) if You Share
- all or a substantial portion of the contents of the database.
-
-For the avoidance of doubt, this Section 4 supplements and does not
-replace Your obligations under this Public License where the Licensed
-Rights include other Copyright and Similar Rights.
-
-
-Section 5 -- Disclaimer of Warranties and Limitation of Liability.
-
- a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
- EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
- AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
- ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
- IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
- WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
- PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
- ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
- KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
- ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
-
- b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
- TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
- NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
- INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
- COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
- USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
- ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
- DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
- IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
-
- c. The disclaimer of warranties and limitation of liability provided
- above shall be interpreted in a manner that, to the extent
- possible, most closely approximates an absolute disclaimer and
- waiver of all liability.
-
-
-Section 6 -- Term and Termination.
-
- a. This Public License applies for the term of the Copyright and
- Similar Rights licensed here. However, if You fail to comply with
- this Public License, then Your rights under this Public License
- terminate automatically.
-
- b. Where Your right to use the Licensed Material has terminated under
- Section 6(a), it reinstates:
-
- 1. automatically as of the date the violation is cured, provided
- it is cured within 30 days of Your discovery of the
- violation; or
-
- 2. upon express reinstatement by the Licensor.
-
- For the avoidance of doubt, this Section 6(b) does not affect any
- right the Licensor may have to seek remedies for Your violations
- of this Public License.
-
- c. For the avoidance of doubt, the Licensor may also offer the
- Licensed Material under separate terms or conditions or stop
- distributing the Licensed Material at any time; however, doing so
- will not terminate this Public License.
-
- d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
- License.
-
-
-Section 7 -- Other Terms and Conditions.
-
- a. The Licensor shall not be bound by any additional or different
- terms or conditions communicated by You unless expressly agreed.
-
- b. Any arrangements, understandings, or agreements regarding the
- Licensed Material not stated herein are separate from and
- independent of the terms and conditions of this Public License.
-
-
-Section 8 -- Interpretation.
-
- a. For the avoidance of doubt, this Public License does not, and
- shall not be interpreted to, reduce, limit, restrict, or impose
- conditions on any use of the Licensed Material that could lawfully
- be made without permission under this Public License.
-
- b. To the extent possible, if any provision of this Public License is
- deemed unenforceable, it shall be automatically reformed to the
- minimum extent necessary to make it enforceable. If the provision
- cannot be reformed, it shall be severed from this Public License
- without affecting the enforceability of the remaining terms and
- conditions.
-
- c. No term or condition of this Public License will be waived and no
- failure to comply consented to unless expressly agreed to by the
- Licensor.
-
- d. Nothing in this Public License constitutes or may be interpreted
- as a limitation upon, or waiver of, any privileges and immunities
- that apply to the Licensor or You, including from the legal
- processes of any jurisdiction or authority.
-
-
-=======================================================================
-
-Creative Commons is not a party to its public licenses.
-Notwithstanding, Creative Commons may elect to apply one of its public
-licenses to material it publishes and in those instances will be
-considered the "Licensor." Except for the limited purpose of indicating
-that material is shared under a Creative Commons public license or as
-otherwise permitted by the Creative Commons policies published at
-creativecommons.org/policies, Creative Commons does not authorize the
-use of the trademark "Creative Commons" or any other trademark or logo
-of Creative Commons without its prior written consent including,
-without limitation, in connection with any unauthorized modifications
-to any of its public licenses or any other arrangements,
-understandings, or agreements concerning use of licensed material. For
-the avoidance of doubt, this paragraph does not form part of the public
-licenses.
-
-Creative Commons may be contacted at creativecommons.org.
\ No newline at end of file
=====================================
gajim/data/emoticons/twemoji/twemoji.png deleted
=====================================
Binary files a/gajim/data/emoticons/twemoji/twemoji.png and /dev/null differ
=====================================
gajim/data/gui/message_actions_box.ui
=====================================
@@ -143,6 +143,7 @@
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="tooltip-text" translatable="yes">Show a list of emojis</property>
+ <property name="action-name">win.show-emoji-chooser</property>
<property name="relief">none</property>
<child>
<object class="GtkImage">
=====================================
gajim/data/gui/preferences.ui
=====================================
@@ -688,57 +688,7 @@
</packing>
</child>
<child>
- <!-- n-columns=3 n-rows=3 -->
- <object class="GtkGrid" id="emoji">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
- <property name="orientation">vertical</property>
- <property name="row-spacing">12</property>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
- <property name="label" translatable="yes">Emoji</property>
- <property name="xalign">0</property>
- <style>
- <class name="bold"/>
- </style>
- </object>
- <packing>
- <property name="left-attach">0</property>
- <property name="top-attach">0</property>
- </packing>
- </child>
- <child>
- <placeholder/>
- </child>
- <child>
- <placeholder/>
- </child>
- <child>
- <placeholder/>
- </child>
- <child>
- <placeholder/>
- </child>
- <child>
- <placeholder/>
- </child>
- <child>
- <placeholder/>
- </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>
+ <placeholder/>
</child>
</object>
</child>
=====================================
gajim/data/style/gajim.css
=====================================
@@ -748,18 +748,6 @@ #NotificationPopupTitle {
font-weight: bold;
}
-/* Emoticon Popover (ModifierChooser) */
-popover#EmoticonPopover button { background: none; border: none; box-shadow:none; padding: 0px;}
-popover#EmoticonPopover button > label { font-size: 2.0rem; }
-popover#EmoticonPopover flowboxchild > label { font-size: 2.0rem; }
-popover#EmoticonPopover flowboxchild { padding-top: 5px; padding-bottom: 5px; }
-popover#EmoticonPopover scrolledwindow { border: none; }
-popover#EmoticonPopover { padding: 5px; background-color: @theme_unfocused_base_color; }
-
-.emoji-chooser-heading { font-size: 1.15rem; font-weight: bold; padding: 5px;}
-.emoji-chooser-flowbox { padding-left: 5px; padding-right: 11px; }
-.emoji-modifier-chooser-flowbox { padding-left: 5px; }
-
/* Settings widgets ======================================================== */
/* SettingsBox */
=====================================
gajim/gtk/application.py
=====================================
@@ -244,12 +244,6 @@ def _startup(self) -> None:
idle.Monitor.set_interval(app.settings.get('autoawaytime') * 60,
app.settings.get('autoxatime') * 60)
- if sys.platform == 'darwin':
- # TODO: Remove if colored emoji rendering works well on
- # Windows and MacOS
- from gajim.gui.emoji_chooser import emoji_chooser
- emoji_chooser.load()
-
from gajim.gui_interface import Interface
self.interface = Interface()
=====================================
gajim/gtk/chat_stack.py
=====================================
@@ -253,7 +253,7 @@ def show_chat(self, account: str, jid: JID) -> None:
'switch_contact', self._current_contact)
if old_primary_clipboard is not None:
- GLib.idle_add(clipboard.set_text, # pyright: ignore
+ GLib.idle_add(clipboard.set_text,
old_primary_clipboard,
-1)
@@ -712,7 +712,6 @@ def _on_function_message(self,
self._chat_control.add_info_message(message)
def _on_send_message(self) -> None:
- self._message_action_box.msg_textview.replace_emojis()
message = self._message_action_box.msg_textview.get_text()
if message.startswith('//'):
# Escape sequence for chat commands
=====================================
gajim/gtk/conversation/plain_widget.py
=====================================
@@ -14,32 +14,19 @@
from __future__ import annotations
-from typing import Any
-
import logging
-import sys
from gi.repository import Gtk
from gi.repository import Pango
from gi.repository import Gdk
-from gi.repository import GdkPixbuf
from gi.repository import GLib
-from gi.repository import GObject
-from gajim.common import app
-from gajim.common.const import StyleAttr
from gajim.common.helpers import open_uri
-from gajim.common.helpers import parse_uri
-from gajim.common.structs import URI
from gajim.common.styling import BaseHyperlink
from gajim.common.styling import PlainBlock
from gajim.common.styling import process_uris
from ..menus import get_conv_action_context_menu
-from ..menus import get_conv_uri_context_menu
-from ..emoji_data import emoji_pixbufs
-from ..emoji_data import get_emoji_pixbuf
-from ..util import get_cursor
from ..util import make_pango_attributes
log = logging.getLogger('gajim.gui.conversaion.plain_widget')
@@ -55,22 +42,11 @@ def __init__(self, account: str, selectable: bool) -> None:
self._account = account
- # We use a Gtk.Textview on MacOS, since there is no support
- # for rendering color fonts (Emojis) on MacOS yet, see:
- # https://gitlab.freedesktop.org/cairo/cairo/-/merge_requests/289
- # https://gitlab.freedesktop.org/cairo/cairo/-/merge_requests/9
- if sys.platform == 'darwin':
- if app.settings.get('dev_use_message_label'):
- self._text_widget = MessageLabel(self._account, selectable)
- else:
- self._text_widget = MessageTextview(self._account)
- else:
- self._text_widget = MessageLabel(self._account, selectable)
+ self._text_widget = MessageLabel(self._account, selectable)
self.add(self._text_widget)
def set_selectable(self, selectable: bool) -> None:
- if isinstance(self._text_widget, MessageLabel):
- self._text_widget.set_selectable(selectable)
+ self._text_widget.set_selectable(selectable)
def add_content(self, block: PlainBlock) -> None:
self._text_widget.print_text_with_styling(block)
@@ -152,295 +128,3 @@ def _on_focus_out(widget: MessageLabel,
_event: Gdk.EventFocus
) -> None:
widget.get_style_context().add_class('transparent-selection')
-
-
-class MessageTextview(Gtk.TextView):
- def __init__(self, account: str) -> None:
- Gtk.TextView.__init__(self)
- self.set_hexpand(True)
- self.set_margin_start(0)
- self.set_margin_end(0)
- self.set_border_width(0)
- self.set_left_margin(0)
- self.set_right_margin(0)
- self.set_has_tooltip(True)
- self.set_editable(False)
- self.set_cursor_visible(False)
- self.set_wrap_mode(Gtk.WrapMode.WORD_CHAR)
-
- self._handlers: dict[int, MessageTextview] = {}
-
- id_ = self.connect('query-tooltip', self._query_tooltip)
- self._handlers[id_] = self
- id_ = self.connect('button-press-event', self._on_button_press)
- self._handlers[id_] = self
- id_ = self.connect('populate-popup', self._on_populate_popup)
- self._handlers[id_] = self
-
- self._account = account
-
- # Used for changing the mouse pointer when hovering clickable URIs
- self._cursor_changed: bool = False
-
- # Keeps text selections for quoting and search actions
- self._selected_text: str = ''
-
- self.get_style_context().add_class('gajim-conversation-text')
-
- # Create Tags
- self._create_url_tags()
- self.get_buffer().create_tag('strong', weight=Pango.Weight.BOLD)
- self.get_buffer().create_tag('emphasis', style=Pango.Style.ITALIC)
- self.get_buffer().create_tag('strike', strikethrough=True)
- self.get_buffer().create_tag('pre', family='monospace')
-
- self.connect('destroy', self._on_destroy)
-
- def _on_destroy(self, *args: Any) -> None:
- for id_, widget in self._handlers.items():
- if widget.handler_is_connected(id_):
- widget.disconnect(id_)
- self._handlers.clear()
-
- def _create_url_tags(self) -> None:
- color = app.css_config.get_value('.gajim-url', StyleAttr.COLOR)
- for name in URI_TAGS:
- tag = self.get_buffer().create_tag(name,
- foreground=color,
- underline=Pango.Underline.SINGLE)
- tag.connect('event', self._on_uri_clicked, tag)
-
- def clear(self) -> None:
- buffer_ = self.get_buffer()
- start, end = buffer_.get_bounds()
- buffer_.delete(start, end)
-
- def get_text(self) -> str:
- buffer_ = self.get_buffer()
- start, end = buffer_.get_bounds()
- return buffer_.get_text(start, end, False)
-
- def print_text_with_styling(self, block: PlainBlock) -> None:
- buffer_ = self.get_buffer()
- buffer_.insert(buffer_.get_start_iter(), block.text.strip())
-
- if len(self.get_text()) > 10000:
- # Limit message styling processing
- return
-
- for span in block.spans:
- start_iter = buffer_.get_iter_at_offset(span.start)
- end_iter = buffer_.get_iter_at_offset(span.end)
- buffer_.apply_tag_by_name(span.name, start_iter, end_iter)
-
- for uri in block.uris:
- start_iter = buffer_.get_iter_at_offset(uri.start)
- end_iter = buffer_.get_iter_at_offset(uri.end)
- buffer_.apply_tag_by_name(uri.name, start_iter, end_iter)
-
- for emoji in block.emojis:
- start_iter = buffer_.get_iter_at_offset(emoji.start)
- end_iter = buffer_.get_iter_at_offset(emoji.end)
- if emoji_pixbufs.complete:
- # Only search for pixbuf if loading is completed
- pixbuf = get_emoji_pixbuf(emoji.text)
- if pixbuf is None:
- buffer_.insert(end_iter, emoji.text)
- else:
- pixbuf = pixbuf.copy()
- anchor = buffer_.create_child_anchor(end_iter)
- anchor.plaintext = emoji.text # type: ignore
- img = Gtk.Image.new_from_pixbuf(pixbuf)
- img.show()
- self.add_child_at_anchor(img, anchor)
- buffer_.delete(
- buffer_.get_iter_at_offset(emoji.start),
- buffer_.get_iter_at_offset(emoji.end))
- else:
- # Set marks and save them so we can replace emojis
- # once loading is complete
- start_mark = buffer_.create_mark(None, end_iter, True)
- buffer_.insert(end_iter, emoji.text)
- end_mark = buffer_.create_mark(None, end_iter, True)
- emoji_pixbufs.append_marks(
- self, start_mark, end_mark, emoji.text)
-
- def replace_emoji(self,
- start_mark: Gtk.TextMark,
- end_mark: Gtk.TextMark,
- pixbuf: GdkPixbuf.Pixbuf,
- codepoint: str
- ) -> None:
- buffer_ = self.get_buffer()
- start_iter = buffer_.get_iter_at_mark(start_mark)
- end_iter = buffer_.get_iter_at_mark(end_mark)
- buffer_.delete(start_iter, end_iter)
-
- anchor = buffer_.create_child_anchor(start_iter)
- anchor.plaintext = codepoint # type: ignore
- img = Gtk.Image.new_from_pixbuf(pixbuf)
- img.show()
- self.add_child_at_anchor(img, anchor)
- buffer_.delete_mark(start_mark)
- buffer_.delete_mark(end_mark)
-
- def add_action_phrase(self, text: str, nickname: str) -> None:
- text = text.replace('/me', f'* {nickname}', 1)
-
- buffer_ = self.get_buffer()
- buffer_.insert(buffer_.get_start_iter(), text.strip())
-
- uris = process_uris(text)
- for uri in uris:
- start_iter = buffer_.get_iter_at_offset(uri.start)
- end_iter = buffer_.get_iter_at_offset(uri.end)
- buffer_.apply_tag_by_name(uri.name, start_iter, end_iter)
-
- start_iter = buffer_.get_start_iter()
- end_iter = buffer_.get_end_iter()
- buffer_.apply_tag_by_name('emphasis', start_iter, end_iter)
-
- def _query_tooltip(self,
- widget: Gtk.TextView,
- x_pos: int,
- y_pos: int,
- _keyboard_mode: bool,
- _tooltip: Gtk.Tooltip
- ) -> bool:
-
- window = widget.get_window(Gtk.TextWindowType.TEXT)
- assert window is not None
- x_pos, y_pos = self.window_to_buffer_coords(
- Gtk.TextWindowType.TEXT, x_pos, y_pos)
-
- iter_ = self.get_iter_at_position(x_pos, y_pos)[1]
- for tag in iter_.get_tags():
- tag_name = tag.get_property('name')
- if tag_name in URI_TAGS:
- window.set_cursor(get_cursor('pointer'))
- self._cursor_changed = True
- return False
-
- if self._cursor_changed:
- window.set_cursor(get_cursor('text'))
- self._cursor_changed = False
- return False
-
- def _on_button_press(self, _widget: Any, event: Gdk.EventButton) -> bool:
- '''
- We don’t open the standard context menu when receiving
- a click on tagged text.
- If it’s untagged text, check if something is selected
- '''
- self._selected_text = ''
-
- if event.button != 3:
- # If it’s not a right click
- return False
-
- x_pos, y_pos = self.window_to_buffer_coords(
- Gtk.TextWindowType.TEXT,
- int(event.x),
- int(event.y))
- _, iter_ = self.get_iter_at_location(x_pos, y_pos)
- tags = iter_.get_tags()
-
- if tags:
- # A tagged text fragment has been clicked
- for tag in tags:
- if tag.get_property('name') in URI_TAGS:
- # Block regular context menu
- return True
-
- # Check if there is a selection and make it available for
- # _on_populate_popup
- buffer_ = self.get_buffer()
- return_val = buffer_.get_selection_bounds()
- if return_val:
- # Something has been selected, get the text
- start_sel, finish_sel = return_val[0], return_val[1]
- self._selected_text = buffer_.get_text(
- start_sel, finish_sel, True)
- elif iter_.get_char() and ord(iter_.get_char()) > 31:
- # Clicked on a word, take whole word for selection
- start_sel = iter_.copy()
- if not start_sel.starts_word():
- start_sel.backward_word_start()
- finish_sel = iter_.copy()
- if not finish_sel.ends_word():
- finish_sel.forward_word_end()
- self._selected_text = buffer_.get_text(
- start_sel, finish_sel, True)
- return False
-
- def _on_populate_popup(self,
- _textview: Gtk.TextView,
- menu: Gtk.Menu
- ) -> None:
- '''
- Overrides the default context menu.
- If text is selected, a submenu with actions on the selection is added.
- (see _on_button_press)
- '''
- if not self._selected_text:
- menu.show_all()
- return
-
- action_menu_item = get_conv_action_context_menu(
- self._account, self._selected_text)
- menu.prepend(action_menu_item)
- menu.show_all()
-
- def _on_uri_clicked(self,
- texttag: Gtk.TextTag,
- _widget: Any,
- event: Gdk.Event,
- iter_: Gtk.TextIter,
- _kind: Gtk.TextTag
- ) -> int:
- if event.type != Gdk.EventType.BUTTON_PRESS:
- return Gdk.EVENT_PROPAGATE
-
- begin_iter = iter_.copy()
- # we get the beginning of the tag
- while not begin_iter.starts_tag(texttag):
- begin_iter.backward_char()
- end_iter = iter_.copy()
- # we get the end of the tag
- while not end_iter.ends_tag(texttag):
- end_iter.forward_char()
-
- # Detect XHTML-IM link
- word = getattr(texttag, 'href', None)
- if not word:
- word = self.get_buffer().get_text(begin_iter, end_iter, True)
-
- uri = parse_uri(word)
- if event.button.button == 3: # right click
- self._show_uri_context_menu(uri)
- return Gdk.EVENT_STOP
-
- # TODO:
- # self.plugin_modified = False
- # app.plugin_manager.extension_point(
- # 'hyperlink_handler', uri, self, self.get_toplevel())
- # if self.plugin_modified:
- # return Gdk.EVENT_STOP
-
- open_uri(uri, account=self._account)
- return Gdk.EVENT_STOP
-
- def _show_uri_context_menu(self, uri: URI) -> None:
- menu = get_conv_uri_context_menu(self._account, uri)
- if menu is None:
- log.warning('No handler for URI type: %s', uri)
- return
-
- def _destroy(menu: Gtk.Menu, _pspec: GObject.ParamSpec) -> None:
- visible = menu.get_property('visible')
- if not visible:
- GLib.idle_add(menu.destroy)
-
- menu.attach_to_widget(self, None)
- menu.connect('notify::visible', _destroy)
- menu.popup_at_pointer()
=====================================
gajim/gtk/emoji_chooser.py deleted
=====================================
@@ -1,412 +0,0 @@
-# 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/>.
-
-import logging
-import weakref
-from collections import OrderedDict
-
-from gi.repository import Gtk
-from gi.repository import GLib
-from gi.repository import GdkPixbuf
-from gi.repository import Pango
-
-from gajim.common import app
-from gajim.common import helpers
-from gajim.common import configpaths
-
-from .builder import get_builder
-from .emoji_data import emoji_data
-from .emoji_data import emoji_pixbufs
-from .emoji_data import Emoji
-
-MODIFIER_MAX_CHILDREN_PER_LINE = 6
-MAX_CHILDREN_PER_LINE = 10
-
-log = logging.getLogger('gajim.emoji')
-
-
-class Section(Gtk.Box):
- def __init__(self, name, search_entry, press_cb, chooser):
- Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL)
- self._chooser = chooser
- self._press_cb = press_cb
- self.pixbuf_generator = None
- self.heading = Gtk.Label(label=name)
- self.heading.set_halign(Gtk.Align.START)
- self.heading.get_style_context().add_class('emoji-chooser-heading')
- self.add(self.heading)
-
- self.flowbox = Gtk.FlowBox()
- self.flowbox.get_style_context().add_class('emoji-chooser-flowbox')
- self.flowbox.set_max_children_per_line(MAX_CHILDREN_PER_LINE)
- self.flowbox.set_filter_func(self._filter_func, search_entry)
- self.flowbox.connect('child-activated', press_cb)
-
- self.add(self.flowbox)
- self.show_all()
-
- def _filter_func(self, child, search_entry):
- name = search_entry.get_text()
- if not name:
- self.show()
- return True
-
- if name in child.desc:
- self.show()
- return True
- return False
-
- def add_emoji(self, codepoint, attrs):
- # Return always True, this method is used for the emoji factory
- # called by GLib.idle_add()
- pixbuf = self._get_next_pixbuf()
-
- variations = attrs.get('variations', None)
- if variations is None:
- if pixbuf is None:
- return True
- self.flowbox.add(EmojiChild(codepoint, pixbuf, attrs['desc']))
- if pixbuf != 'font':
- # We save the pixbuf for fast access if we need to
- # replace a codepoint in the textview
- emoji_pixbufs[codepoint] = pixbuf
- else:
- if pixbuf is not None:
- chooser = ModifierChooser()
-
- # Iterate over the variations and add the codepoints
- for codepoint_ in variations.keys():
- pixbuf_ = self._get_next_pixbuf()
- if pixbuf_ is None:
- continue
-
- if pixbuf_ == 'font':
- if not self._chooser.font_supports_codepoint(
- codepoint_):
- continue
- else:
- emoji_pixbufs[codepoint_] = pixbuf_
-
- # Only codepoints are added which the
- # font or theme supports
- chooser.add_emoji(codepoint_, pixbuf_)
-
- # Check if we successfully added codepoints with modifiers
- if chooser.has_child:
- # If we have children then add a button
- # and set the popover
- child = EmojiModifierChild(
- codepoint, pixbuf, attrs['desc'])
- child.button.set_popover(chooser)
- chooser.flowbox.connect(
- 'child-activated', self._press_cb)
- else:
- # If no children were added, destroy the chooser
- # and add a EmojiChild instead of a EmojiModifierChild
- chooser.destroy()
- child = EmojiChild(codepoint, pixbuf, attrs['desc'])
-
- if pixbuf != 'font':
- emoji_pixbufs[codepoint] = pixbuf
-
- self.flowbox.add(child)
- else:
- # We don’t have a image for the base codepoint
- # so skip all modifiers of it
- for _entry in variations:
- pixbuf = self._get_next_pixbuf()
- return True
-
- def clear_emojis(self):
- def _remove_emoji(emoji):
- self.flowbox.remove(emoji)
- emoji.destroy()
- self.flowbox.foreach(_remove_emoji)
-
- def _get_next_pixbuf(self):
- if self.pixbuf_generator is None:
- return 'font'
- return next(self.pixbuf_generator, False)
-
-
-class EmojiChild(Gtk.FlowBoxChild):
- def __init__(self, codepoint, pixbuf, desc):
- Gtk.FlowBoxChild.__init__(self)
- self.desc = desc
- self.codepoint = codepoint
- self.pixbuf = pixbuf
- if pixbuf == 'font':
- self.add(Gtk.Label(label=codepoint))
- else:
- self.add(Gtk.Image.new_from_pixbuf(pixbuf))
- self.set_tooltip_text(desc)
- self.show_all()
-
- def get_emoji(self):
- if self.pixbuf != 'font':
- pixbuf = self.get_child().get_pixbuf()
- pixbuf = pixbuf.scale_simple(Emoji.INPUT_SIZE,
- Emoji.INPUT_SIZE,
- GdkPixbuf.InterpType.HYPER)
- return self.codepoint, pixbuf
- return self.codepoint, None
-
-
-class EmojiModifierChild(Gtk.FlowBoxChild):
- def __init__(self, codepoint, pixbuf, desc):
- Gtk.FlowBoxChild.__init__(self)
- self.desc = desc
- self.codepoint = codepoint
- self.pixbuf = pixbuf
-
- self.button = Gtk.MenuButton()
- self.button.set_relief(Gtk.ReliefStyle.NONE)
- self.button.connect('button-press-event', self._button_press)
-
- if pixbuf == 'font':
- self.button.remove(self.button.get_child())
- label = Gtk.Label(label=codepoint)
- self.button.add(label)
- else:
- self.button.get_child().set_from_pixbuf(pixbuf)
-
- self.set_tooltip_text(desc)
- self.add(self.button)
- self.show_all()
-
- def _button_press(self, button, event):
- if event.button == 3:
- button.get_popover().show()
- button.get_popover().get_child().unselect_all()
- return True
- if event.button == 1:
- self.get_parent().emit('child-activated', self)
- return True
- return False
-
- def get_emoji(self):
- if self.pixbuf != 'font':
- pixbuf = self.button.get_child().get_pixbuf()
- pixbuf = pixbuf.scale_simple(Emoji.INPUT_SIZE,
- Emoji.INPUT_SIZE,
- GdkPixbuf.InterpType.HYPER)
- return self.codepoint, pixbuf
- return self.codepoint, None
-
-
-class ModifierChooser(Gtk.Popover):
- def __init__(self):
- Gtk.Popover.__init__(self)
- self.set_name('EmoticonPopover')
- self._has_child = False
-
- self.flowbox = Gtk.FlowBox()
- self.flowbox.get_style_context().add_class(
- 'emoji-modifier-chooser-flowbox')
- self.flowbox.set_size_request(200, -1)
- self.flowbox.set_max_children_per_line(MODIFIER_MAX_CHILDREN_PER_LINE)
- self.flowbox.show()
- self.add(self.flowbox)
-
- @property
- def has_child(self):
- return self._has_child
-
- def add_emoji(self, codepoint, pixbuf):
- self.flowbox.add(EmojiChild(codepoint, pixbuf, None))
- self._has_child = True
-
-
-class EmojiChooser(Gtk.Popover):
-
- _section_names = [
- 'Smileys & People',
- 'Animals & Nature',
- 'Food & Drink',
- 'Travel & Places',
- 'Activities',
- 'Objects',
- 'Symbols',
- 'Flags'
- ]
-
- def __init__(self):
- super().__init__()
- self.set_name('EmoticonPopover')
- self._text_widget = None
- self._load_source_id = None
- self._pango_layout = Pango.Layout(self.get_pango_context())
-
- self._builder = get_builder('emoji_chooser.ui')
- self._search = self._builder.get_object('search')
- self._stack = self._builder.get_object('stack')
-
- self._sections = OrderedDict()
- for name in self._section_names:
- self._sections[name] = Section(
- name, self._search, self._on_emoticon_press, self)
-
- section_box = self._builder.get_object('section_box')
- for section in self._sections.values():
- section_box.add(section)
-
- self.add(self._builder.get_object('box'))
-
- self.connect('key-press-event', self._key_press)
- self._builder.connect_signals(self)
-
- @property
- def text_widget(self):
- return self._text_widget
-
- @text_widget.setter
- def text_widget(self, value):
- # Hold only a weak reference so if we can destroy
- # the ChatControl
- self._text_widget = weakref.ref(value)
-
- def _key_press(self, _widget, event):
- return self._search.handle_event(event)
-
- def _search_changed(self, _entry):
- for section in self._sections.values():
- section.hide()
- section.flowbox.invalidate_filter()
- self._switch_stack()
-
- def _clear_sections(self):
- for section in self._sections.values():
- section.clear_emojis()
-
- def _switch_stack(self):
- for section in self._sections.values():
- if section.is_visible():
- self._stack.set_visible_child_name('list')
- return
- self._stack.set_visible_child_name('not-found')
-
- @staticmethod
- def _get_current_theme():
- theme = app.settings.get('emoticons_theme')
- themes = helpers.get_available_emoticon_themes()
- if theme not in themes:
- app.settings.set('emoticons_theme', 'noto')
- theme = 'noto'
- return theme
-
- @staticmethod
- def _get_emoji_theme_path(theme):
- if theme == 'font':
- return 'font'
-
- base_path = configpaths.get('EMOTICONS')
- emoticons_data_path = base_path / theme / f'{theme}.png'
- if emoticons_data_path.exists():
- return emoticons_data_path
-
- emoticons_user_path = configpaths.get('MY_EMOTS') / f'{theme}.png'
- if emoticons_user_path.exists():
- return emoticons_user_path
-
- log.warning('Could not find emoji theme: %s', theme)
- return None
-
- def load(self):
- theme = self._get_current_theme()
- path = self._get_emoji_theme_path(theme)
- if not theme or path is None:
- self._clear_sections()
- emoji_pixbufs.clear()
- return
-
- # Attach pixbuf generator
- pixbuf_generator = None
- if theme != 'font':
- pixbuf_generator = self._get_next_pixbuf(path)
- for section in self._sections.values():
- section.pixbuf_generator = pixbuf_generator
-
- if self._load_source_id is not None:
- GLib.source_remove(self._load_source_id)
-
- # When we change emoji theme
- self._clear_sections()
- emoji_pixbufs.clear()
-
- factory = self._emoji_factory(theme == 'font')
- self._load_source_id = GLib.idle_add(lambda: next(factory, False),
- priority=GLib.PRIORITY_LOW)
-
- def _emoji_factory(self, font):
- for codepoint, attrs in emoji_data.items():
- if not attrs['fully-qualified']:
- # We don’t add these to the UI
- continue
-
- if font and not self.font_supports_codepoint(codepoint):
- continue
-
- section = self._sections[attrs['group']]
- yield section.add_emoji(codepoint, attrs)
- self._load_source_id = None
- emoji_pixbufs.complete = True
-
- def font_supports_codepoint(self, codepoint):
- self._pango_layout.set_text(codepoint, -1)
- if self._pango_layout.get_unknown_glyphs_count():
- return False
- if len(codepoint) > 1:
- # The font supports each of the codepoints
- # Check if the rendered glyph is more than one char
- if self._pango_layout.get_size()[0] > 19000:
- return False
- return True
-
- @staticmethod
- def _get_next_pixbuf(path):
- src_x = src_y = cur_column = 0
- atlas = GdkPixbuf.Pixbuf.new_from_file(str(path))
-
- while True:
- src_x = cur_column * Emoji.PARSE_WIDTH
- subpixbuf = atlas.new_subpixbuf(src_x, src_y,
- Emoji.PARSE_WIDTH,
- Emoji.PARSE_HEIGHT)
-
- if list(subpixbuf.get_pixels())[0:4] == [0, 0, 0, 255]:
- # top left corner is a black pixel means image is missing
- subpixbuf = None
-
- if cur_column == Emoji.PARSE_COLUMNS - 1:
- src_y += Emoji.PARSE_WIDTH
- cur_column = 0
- else:
- cur_column += 1
-
- yield subpixbuf
-
- def _on_emoticon_press(self, flowbox, child):
- GLib.timeout_add(100, flowbox.unselect_child, child)
- codepoint, pixbuf = child.get_emoji()
- self._text_widget().insert_emoji(codepoint, pixbuf)
- self._text_widget().grab_focus()
-
- def do_destroy(self):
- # Remove the references we hold to other objects
- self._text_widget = None
- # Never destroy, creating a new EmoticonPopover is expensive
- return True
-
-
-emoji_chooser = EmojiChooser()
=====================================
gajim/gtk/emoji_data.py deleted
=====================================
The diff for this file was not included because it is too large.
=====================================
gajim/gtk/emoji_data.pyi deleted
=====================================
@@ -1,44 +0,0 @@
-# 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 enum import IntEnum
-from collections import OrderedDict
-
-from gi.repository import GdkPixbuf
-from gi.repository import Gtk
-
-class Emoji(IntEnum):
- ...
-
-def is_emoji(codepoints: str) -> bool: ...
-def get_emoji_pixbuf(codepoints: str) -> GdkPixbuf.Pixbuf: ...
-
-class EmojiData(OrderedDict):
- def get_regex(self) -> str: ...
-
-class EmojiPixbufs(dict):
- @property
- def complete(self) -> bool: ...
- @complete.setter
- def complete(self, value: bool) -> None: ...
- def clear(self) -> None: ...
- def append_marks(self,
- textview: Gtk.TextView,
- start: Gtk.TextMark,
- end: Gtk.TextMark,
- codepoint: str) -> None: ...
-
-emoji_pixbufs: EmojiPixbufs
-emoji_ascii_data: dict[str, str]
-emoji_data: EmojiData
=====================================
gajim/gtk/message_actions_box.py
=====================================
@@ -18,7 +18,6 @@
from typing import Optional
import os
-import sys
import tempfile
import logging
import uuid
@@ -45,7 +44,6 @@
from .builder import get_builder
from .dialogs import ErrorDialog
-from .emoji_chooser import emoji_chooser
from .menus import get_encryption_menu
from .menus import get_format_menu
from .menus import get_groupchat_menu
@@ -98,8 +96,6 @@ def __init__(self) -> None:
self._ui.quick_invite_button.set_action_name('win.muc-invite')
- self._init_emoticon_popover()
-
self.show_all()
self._ui.connect_signals(self)
@@ -157,7 +153,7 @@ def _on_action(self,
self._on_format(action_name)
elif action_name == 'show-emoji-chooser':
- self._on_show_emoji_chooser()
+ self.msg_textview.emit('insert-emoji')
elif action_name == 'quote':
assert param
@@ -389,31 +385,6 @@ def _set_settings_menu(self, contact: ChatContactT) -> None:
menu = get_singlechat_menu(contact)
self._ui.settings_menu.set_menu_model(menu)
- def _init_emoticon_popover(self) -> None:
- if not app.settings.get('emoticons_theme'):
- return
-
- if sys.platform == 'darwin':
- emoji_chooser.text_widget = self.msg_textview
- self._ui.emoticons_button.set_popover(emoji_chooser)
- return
-
- self._ui.emoticons_button.set_sensitive(True)
- self._ui.emoticons_button.connect('clicked',
- self._on_emoticon_button_clicked)
-
- def toggle_emoticons(self) -> None:
- if app.settings.get('emoticons_theme'):
- self._ui.emoticons_button.set_no_show_all(False)
- self._ui.emoticons_button.show()
- else:
- self._ui.emoticons_button.set_no_show_all(True)
- self._ui.emoticons_button.hide()
-
- def _on_emoticon_button_clicked(self, _widget: Gtk.Button) -> None:
- self.msg_textview.emit('insert-emoji')
- self._ui.emoticons_button.set_active(False)
-
def _on_format(self, name: str) -> None:
name = name.removeprefix('input-')
self.msg_textview.apply_formatting(name)
@@ -421,14 +392,6 @@ def _on_format(self, name: str) -> None:
def _on_clear(self) -> None:
self.msg_textview.clear()
- def _on_show_emoji_chooser(self) -> None:
- if sys.platform == 'darwin':
- popover = self._ui.emoticons_button.get_popover()
- assert popover
- popover.show()
- else:
- self.msg_textview.emit('insert-emoji')
-
def _on_send_file_enabled_changed(self,
action: Gio.SimpleAction,
_param: GObject.ParamSpec) -> None:
=====================================
gajim/gtk/message_input.py
=====================================
@@ -25,11 +25,9 @@
from collections import defaultdict
import logging
-import sys
from gi.repository import Gtk
from gi.repository import Gdk
-from gi.repository import GdkPixbuf
from gi.repository import GLib
from gi.repository import Pango
@@ -420,60 +418,6 @@ def apply_formatting(self, formatting: str) -> None:
buf.get_iter_at_offset(start_offset),
buf.get_iter_at_offset(end_offset + 2))
- def replace_emojis(self) -> None:
- if sys.platform != 'darwin':
- # TODO: Remove if colored emoji rendering works well on MacOS
- return
-
- theme = app.settings.get('emoticons_theme')
- if not theme or theme == 'font':
- return
-
- def _replace(anchor: Gtk.TextChildAnchor) -> None:
- if anchor is None:
- return
- image = anchor.get_widgets()[0]
- if hasattr(image, 'codepoint'):
- # found emoji
- codepoint = getattr(image, 'codepoint')
- self._replace_char_at_iter(iter_, codepoint)
- image.destroy()
-
- iter_ = self.get_buffer().get_start_iter()
- _replace(iter_.get_child_anchor())
-
- while iter_.forward_char():
- _replace(iter_.get_child_anchor())
-
- def _replace_char_at_iter(self,
- iter_: Gtk.TextIter,
- new_char: str
- ) -> None:
- buf = self.get_buffer()
- iter_2 = iter_.copy()
- iter_2.forward_char()
- buf.delete(iter_, iter_2)
- buf.insert(iter_, new_char)
-
- def insert_emoji(self, codepoint: str, pixbuf: GdkPixbuf.Pixbuf) -> None:
- buf = self.get_buffer()
- if buf.get_char_count():
- # buffer contains text
- buf.insert_at_cursor(' ')
-
- insert_mark = buf.get_insert()
- insert_iter = buf.get_iter_at_mark(insert_mark)
-
- if pixbuf is None:
- buf.insert(insert_iter, codepoint)
- else:
- anchor = buf.create_child_anchor(insert_iter)
- image = Gtk.Image.new_from_pixbuf(pixbuf)
- setattr(image, 'codepoint', codepoint)
- image.show()
- self.add_child_at_anchor(image, anchor)
- buf.insert_at_cursor(' ')
-
def clear(self, *args: Any) -> None:
buf = self.get_buffer()
start, end = buf.get_bounds()
=====================================
gajim/gtk/preferences.py
=====================================
@@ -26,7 +26,6 @@
from gajim.common import app
from gajim.common import configpaths
-from gajim.common import helpers
from gajim.common.const import THRESHOLD_OPTIONS
from gajim.common.i18n import _
from gajim.common.helpers import open_file
@@ -42,7 +41,6 @@
from .const import SettingType
from .dialogs import DialogButton
from .dialogs import ConfirmationDialog
-from .emoji_chooser import emoji_chooser
from .settings import PopoverSetting
from .settings import SettingsBox
from .settings import SettingsDialog
@@ -81,8 +79,6 @@ def __init__(self) -> None:
self.add(self._ui.grid)
- self._check_emoji_theme()
-
prefs: list[tuple[str, Type[PreferenceBox]]] = [
('window_behaviour', WindowBehaviour),
('plugins', Plugins),
@@ -95,7 +91,6 @@ def __init__(self) -> None:
('status_message', StatusMessage),
('automatic_status', AutomaticStatus),
('themes', Themes),
- ('emoji', Emoji),
('server', Server),
('audio', Audio),
('video', Video),
@@ -117,10 +112,6 @@ def __init__(self) -> None:
self._ui.connect_signals(self)
self.show_all()
- if sys.platform != 'darwin':
- # TODO: Remove if colored emoji rendering works well on
- # Windows and MacOS
- self._ui.emoji.hide()
def get_ui(self):
return self._ui
@@ -157,14 +148,6 @@ def update_proxy_list(self) -> None:
miscellaneous = cast(Miscellaneous, self._prefs['miscellaneous'])
miscellaneous.update_proxy_list()
- @staticmethod
- def _check_emoji_theme() -> None:
- # Ensure selected emoji theme is valid
- emoji_themes = helpers.get_available_emoticon_themes()
- settings_theme = app.settings.get('emoticons_theme')
- if settings_theme not in emoji_themes:
- app.settings.set('emoticons_theme', 'font')
-
def _on_destroy(self, _widget: Gtk.Widget) -> None:
self._prefs.clear()
app.check_finalize(self)
@@ -704,42 +687,6 @@ def _on_dark_theme(value: str, *args: Any) -> None:
app.ged.raise_event(StyleChanged())
-class Emoji(PreferenceBox):
- def __init__(self, *args: Any) -> None:
- if sys.platform != 'darwin':
- # TODO: Remove if colored emoji rendering works well on
- # Windows and MacOS
- PreferenceBox.__init__(self, [])
- return
-
- emoji_themes_items: list[str] = []
- for theme in helpers.get_available_emoticon_themes():
- emoji_themes_items.append(theme)
-
- settings = [
- Setting(SettingKind.POPOVER,
- _('Emoji Theme'),
- SettingType.CONFIG,
- 'emoticons_theme',
- desc=_('Choose from various emoji styles'),
- props={'entries': emoji_themes_items},
- callback=self._on_emoticons_theme)
- ]
-
- PreferenceBox.__init__(self, settings)
-
- def _on_emoticons_theme(self, *args: Any) -> None:
- emoji_chooser.load()
- self._toggle_emoticons()
-
- @staticmethod
- def _toggle_emoticons() -> None:
- # TODO
- # for ctrl in app.window.get_controls():
- # ctrl.toggle_emoticons()
- pass
-
-
class Server(PreferenceBox):
def __init__(self, *args: Any) -> None:
=====================================
pyproject.toml
=====================================
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
[tool.codespell]
-skip = "*__pycache__*,gajim/gui,gajim/data/icons,gajim/data/sounds,gajim/data/emoticons"
+skip = "*__pycache__*,gajim/gui,gajim/data/icons,gajim/data/sounds"
ignore-words-list = "claus,pres,ser,trough"
=====================================
setup.cfg
=====================================
@@ -49,8 +49,6 @@ install_requires =
[options.package_data]
gajim =
- data/emoticons/*/*.png
- data/emoticons/*/LICENSE
data/gui/*.ui
data/icons/hicolor/*/*/*.png
data/icons/hicolor/*/*/*.svg
View it on GitLab: https://dev.gajim.org/gajim/gajim/-/compare/3dfcd112926ce812fbb7351e663565b…
--
View it on GitLab: https://dev.gajim.org/gajim/gajim/-/compare/3dfcd112926ce812fbb7351e663565b…
You're receiving this email because of your account on dev.gajim.org.
Daniel Brötzmann pushed to branch remove-old-emoji-code at gajim / gajim
Commits:
3dfcd112 by wurstsalat at 2022-11-30T00:19:54+01:00
refactor: Remove old emoji code
Fixes #10932
- - - - -
23 changed files:
- gajim/common/configpaths.py
- gajim/common/helpers.py
- gajim/common/setting_values.py
- gajim/common/settings.py
- gajim/common/styling.py
- − gajim/data/emoticons/noto/LICENSE
- − gajim/data/emoticons/noto/noto.png
- − gajim/data/emoticons/twemoji/LICENSE
- − gajim/data/emoticons/twemoji/twemoji.png
- gajim/data/gui/message_actions_box.ui
- gajim/data/gui/preferences.ui
- gajim/data/style/gajim.css
- gajim/gtk/application.py
- gajim/gtk/chat_stack.py
- gajim/gtk/conversation/plain_widget.py
- − gajim/gtk/emoji_chooser.py
- − gajim/gtk/emoji_data.py
- − gajim/gtk/emoji_data.pyi
- gajim/gtk/message_actions_box.py
- gajim/gtk/message_input.py
- gajim/gtk/preferences.py
- pyproject.toml
- setup.cfg
Changes:
=====================================
gajim/common/configpaths.py
=====================================
@@ -125,7 +125,6 @@ def __init__(self) -> None:
source_paths = [
('DATA', basedir / 'data'),
('STYLE', basedir / 'data' / 'style'),
- ('EMOTICONS', basedir / 'data' / 'emoticons'),
('GUI', basedir / 'data' / 'gui'),
('ICONS', basedir / 'data' / 'icons'),
('HOME', Path.home()),
@@ -216,8 +215,6 @@ def init(self):
('PLUGINS_IMAGES', 'plugins_images',
PathLocation.CACHE, PathType.FOLDER),
('PLUGINS_USER', 'plugins', PathLocation.DATA, PathType.FOLDER),
- ('MY_EMOTS',
- 'emoticons', PathLocation.DATA, PathType.FOLDER_OPTIONAL),
('MY_ICONSETS',
'iconsets', PathLocation.DATA, PathType.FOLDER_OPTIONAL),
=====================================
gajim/common/helpers.py
=====================================
@@ -765,22 +765,6 @@ def version_condition(current_version: str, required_version: str) -> bool:
return True
-def get_available_emoticon_themes() -> list[str]:
- files: list[Path] = []
- for folder in configpaths.get('EMOTICONS').iterdir():
- if not folder.is_dir():
- continue
- files += [theme for theme in folder.iterdir() if theme.is_file()]
-
- my_emots = configpaths.get('MY_EMOTS')
- if my_emots.is_dir():
- files += list(my_emots.iterdir())
-
- emoticons_themes = ['font']
- emoticons_themes += [file.stem for file in files if file.suffix == '.png']
- return sorted(emoticons_themes)
-
-
def call_counter(func):
def helper(self, restart=False):
if restart:
=====================================
gajim/common/setting_values.py
=====================================
@@ -48,7 +48,6 @@ class _ACCOUNT_DEFAULT:
'confirm_close_muc',
'confirm_on_window_delete',
'dev_force_bookmark_2',
- 'dev_use_message_label',
'developer_modus',
'enable_emoji_shortcodes',
'enable_negative_priority',
@@ -136,7 +135,6 @@ class _ACCOUNT_DEFAULT:
'date_format',
'date_time_format',
'dictionary_url',
- 'emoticons_theme',
'gc_refer_to_nick_char',
'global_proxy',
'last_save_dir',
@@ -192,7 +190,6 @@ class _ACCOUNT_DEFAULT:
'dev_use_message_label': True,
'developer_modus': False,
'dictionary_url': 'WIKTIONARY',
- 'emoticons_theme': 'noto-emoticons',
'enable_emoji_shortcodes': True,
'enable_negative_priority': False,
'enable_file_preview': True,
=====================================
gajim/common/settings.py
=====================================
@@ -237,11 +237,6 @@ def _setup_installation_defaults() -> None:
if IS_PORTABLE:
APP_SETTINGS['use_keyring'] = False
- if sys.platform in ('win32', 'darwin'):
- # Cairo does not support color emojis fonts on
- # Windows and MacOS
- APP_SETTINGS['dev_use_message_label'] = False
-
def _load_app_overrides(self) -> None:
if not OVERRIDES_PATH.exists():
return
=====================================
gajim/common/styling.py
=====================================
@@ -19,7 +19,6 @@
from typing import Optional
import string
-import sys
import re
from dataclasses import dataclass
from dataclasses import field
@@ -31,7 +30,6 @@
from gajim.common.helpers import parse_uri as analyze_uri
from gajim.common.helpers import validate_jid
from gajim.common.text_helpers import jid_to_iri
-from gajim.gui.emoji_data import emoji_data
PRE = '`'
STRONG = '*'
@@ -52,9 +50,6 @@
URI_OR_JID_RX = re.compile(fr'(?P<uri>{regex.IRI})|(?P<jid>{regex.XMPP.jid})')
-EMOJI_RX = emoji_data.get_regex()
-EMOJI_RX = re.compile(EMOJI_RX)
-
SD = 0
SD_POS = 1
MAX_QUOTE_LEVEL = 20
@@ -96,11 +91,6 @@ class MailAddress(BaseHyperlink):
name: str = field(default='mailadr', init=False)
-@dataclass
-class Emoji(StyleObject):
- name: str = field(default='emoji', init=False)
-
-
@dataclass
class Block(StyleObject):
@@ -116,7 +106,6 @@ class PlainBlock(Block):
name: str = field(default='plain', init=False)
spans: list[Span] = field(default_factory=list)
uris: list[BaseHyperlink] = field(default_factory=list)
- emojis: list[Emoji] = field(default_factory=list)
@classmethod
def from_quote_match(cls, match: Match[str]) -> PlainBlock:
@@ -207,10 +196,6 @@ def process(text: Union[str, bytes], level: int = 0) -> ParsingResult:
for line in block.text.splitlines(keepends=True):
block.spans += _parse_line(line, offset, offset_bytes)
block.uris += _parse_uris(line, offset, offset_bytes)
- if sys.platform == 'darwin':
- # block.emojis is used for replacing emojis with Gtk.Images
- # Necessary for MessageTextview (darwin) only
- block.emojis += _parse_emojis(line, offset)
offset += len(line)
offset_bytes += len(line.encode())
@@ -360,22 +345,6 @@ def make(start: int, end: int, is_jid: bool) -> Optional[BaseHyperlink]:
return uris
-def _parse_emojis(line: str, offset: int) -> list[Emoji]:
- emojis: list[Emoji] = []
-
- def make(match: Match[str]) -> Emoji:
- return _make_emoji(line,
- match.start(),
- match.end() - 1,
- offset)
-
- for match in EMOJI_RX.finditer(line):
- emoji = make(match)
- emojis.append(emoji)
-
- return emojis
-
-
def _handle_pre_span(line: str,
index: int,
offset: int,
@@ -463,18 +432,6 @@ def _make_hyperlink(line: str,
text=text)
-def _make_emoji(line: str,
- start: int,
- end: int,
- offset: int) -> Emoji:
- text = line[start:end + 1]
- start += offset
- end += offset + 1
- return Emoji(start=start,
- end=end,
- text=text)
-
-
def _is_span_empty(sd: str, index: int, stack: list[tuple[str, int]]) -> bool:
start_sd = stack[-1][SD]
if start_sd != sd:
=====================================
gajim/data/emoticons/noto/LICENSE deleted
=====================================
@@ -1,201 +0,0 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
=====================================
gajim/data/emoticons/noto/noto.png deleted
=====================================
Binary files a/gajim/data/emoticons/noto/noto.png and /dev/null differ
=====================================
gajim/data/emoticons/twemoji/LICENSE deleted
=====================================
@@ -1,393 +0,0 @@
-Attribution 4.0 International
-
-=======================================================================
-
-Creative Commons Corporation ("Creative Commons") is not a law firm and
-does not provide legal services or legal advice. Distribution of
-Creative Commons public licenses does not create a lawyer-client or
-other relationship. Creative Commons makes its licenses and related
-information available on an "as-is" basis. Creative Commons gives no
-warranties regarding its licenses, any material licensed under their
-terms and conditions, or any related information. Creative Commons
-disclaims all liability for damages resulting from their use to the
-fullest extent possible.
-
-Using Creative Commons Public Licenses
-
-Creative Commons public licenses provide a standard set of terms and
-conditions that creators and other rights holders may use to share
-original works of authorship and other material subject to copyright
-and certain other rights specified in the public license below. The
-following considerations are for informational purposes only, are not
-exhaustive, and do not form part of our licenses.
-
- Considerations for licensors: Our public licenses are
- intended for use by those authorized to give the public
- permission to use material in ways otherwise restricted by
- copyright and certain other rights. Our licenses are
- irrevocable. Licensors should read and understand the terms
- and conditions of the license they choose before applying it.
- Licensors should also secure all rights necessary before
- applying our licenses so that the public can reuse the
- material as expected. Licensors should clearly mark any
- material not subject to the license. This includes other CC-
- licensed material, or material used under an exception or
- limitation to copyright. More considerations for licensors:
- wiki.creativecommons.org/Considerations_for_licensors
-
- Considerations for the public: By using one of our public
- licenses, a licensor grants the public permission to use the
- licensed material under specified terms and conditions. If
- the licensor's permission is not necessary for any reason--for
- example, because of any applicable exception or limitation to
- copyright--then that use is not regulated by the license. Our
- licenses grant only permissions under copyright and certain
- other rights that a licensor has authority to grant. Use of
- the licensed material may still be restricted for other
- reasons, including because others have copyright or other
- rights in the material. A licensor may make special requests,
- such as asking that all changes be marked or described.
- Although not required by our licenses, you are encouraged to
- respect those requests where reasonable. More_considerations
- for the public:
- wiki.creativecommons.org/Considerations_for_licensees
-
-=======================================================================
-
-Creative Commons Attribution 4.0 International Public License
-
-By exercising the Licensed Rights (defined below), You accept and agree
-to be bound by the terms and conditions of this Creative Commons
-Attribution 4.0 International Public License ("Public License"). To the
-extent this Public License may be interpreted as a contract, You are
-granted the Licensed Rights in consideration of Your acceptance of
-these terms and conditions, and the Licensor grants You such rights in
-consideration of benefits the Licensor receives from making the
-Licensed Material available under these terms and conditions.
-
-
-Section 1 -- Definitions.
-
- a. Adapted Material means material subject to Copyright and Similar
- Rights that is derived from or based upon the Licensed Material
- and in which the Licensed Material is translated, altered,
- arranged, transformed, or otherwise modified in a manner requiring
- permission under the Copyright and Similar Rights held by the
- Licensor. For purposes of this Public License, where the Licensed
- Material is a musical work, performance, or sound recording,
- Adapted Material is always produced where the Licensed Material is
- synched in timed relation with a moving image.
-
- b. Adapter's License means the license You apply to Your Copyright
- and Similar Rights in Your contributions to Adapted Material in
- accordance with the terms and conditions of this Public License.
-
- c. Copyright and Similar Rights means copyright and/or similar rights
- closely related to copyright including, without limitation,
- performance, broadcast, sound recording, and Sui Generis Database
- Rights, without regard to how the rights are labeled or
- categorized. For purposes of this Public License, the rights
- specified in Section 2(b)(1)-(2) are not Copyright and Similar
- Rights.
-
- d. Effective Technological Measures means those measures that, in the
- absence of proper authority, may not be circumvented under laws
- fulfilling obligations under Article 11 of the WIPO Copyright
- Treaty adopted on December 20, 1996, and/or similar international
- agreements.
-
- e. Exceptions and Limitations means fair use, fair dealing, and/or
- any other exception or limitation to Copyright and Similar Rights
- that applies to Your use of the Licensed Material.
-
- f. Licensed Material means the artistic or literary work, database,
- or other material to which the Licensor applied this Public
- License.
-
- g. Licensed Rights means the rights granted to You subject to the
- terms and conditions of this Public License, which are limited to
- all Copyright and Similar Rights that apply to Your use of the
- Licensed Material and that the Licensor has authority to license.
-
- h. Licensor means the individual(s) or entity(ies) granting rights
- under this Public License.
-
- i. Share means to provide material to the public by any means or
- process that requires permission under the Licensed Rights, such
- as reproduction, public display, public performance, distribution,
- dissemination, communication, or importation, and to make material
- available to the public including in ways that members of the
- public may access the material from a place and at a time
- individually chosen by them.
-
- j. Sui Generis Database Rights means rights other than copyright
- resulting from Directive 96/9/EC of the European Parliament and of
- the Council of 11 March 1996 on the legal protection of databases,
- as amended and/or succeeded, as well as other essentially
- equivalent rights anywhere in the world.
-
- k. You means the individual or entity exercising the Licensed Rights
- under this Public License. Your has a corresponding meaning.
-
-
-Section 2 -- Scope.
-
- a. License grant.
-
- 1. Subject to the terms and conditions of this Public License,
- the Licensor hereby grants You a worldwide, royalty-free,
- non-sublicensable, non-exclusive, irrevocable license to
- exercise the Licensed Rights in the Licensed Material to:
-
- a. reproduce and Share the Licensed Material, in whole or
- in part; and
-
- b. produce, reproduce, and Share Adapted Material.
-
- 2. Exceptions and Limitations. For the avoidance of doubt, where
- Exceptions and Limitations apply to Your use, this Public
- License does not apply, and You do not need to comply with
- its terms and conditions.
-
- 3. Term. The term of this Public License is specified in Section
- 6(a).
-
- 4. Media and formats; technical modifications allowed. The
- Licensor authorizes You to exercise the Licensed Rights in
- all media and formats whether now known or hereafter created,
- and to make technical modifications necessary to do so. The
- Licensor waives and/or agrees not to assert any right or
- authority to forbid You from making technical modifications
- necessary to exercise the Licensed Rights, including
- technical modifications necessary to circumvent Effective
- Technological Measures. For purposes of this Public License,
- simply making modifications authorized by this Section 2(a)
- (4) never produces Adapted Material.
-
- 5. Downstream recipients.
-
- a. Offer from the Licensor -- Licensed Material. Every
- recipient of the Licensed Material automatically
- receives an offer from the Licensor to exercise the
- Licensed Rights under the terms and conditions of this
- Public License.
-
- b. No downstream restrictions. You may not offer or impose
- any additional or different terms or conditions on, or
- apply any Effective Technological Measures to, the
- Licensed Material if doing so restricts exercise of the
- Licensed Rights by any recipient of the Licensed
- Material.
-
- 6. No endorsement. Nothing in this Public License constitutes or
- may be construed as permission to assert or imply that You
- are, or that Your use of the Licensed Material is, connected
- with, or sponsored, endorsed, or granted official status by,
- the Licensor or others designated to receive attribution as
- provided in Section 3(a)(1)(A)(i).
-
- b. Other rights.
-
- 1. Moral rights, such as the right of integrity, are not
- licensed under this Public License, nor are publicity,
- privacy, and/or other similar personality rights; however, to
- the extent possible, the Licensor waives and/or agrees not to
- assert any such rights held by the Licensor to the limited
- extent necessary to allow You to exercise the Licensed
- Rights, but not otherwise.
-
- 2. Patent and trademark rights are not licensed under this
- Public License.
-
- 3. To the extent possible, the Licensor waives any right to
- collect royalties from You for the exercise of the Licensed
- Rights, whether directly or through a collecting society
- under any voluntary or waivable statutory or compulsory
- licensing scheme. In all other cases the Licensor expressly
- reserves any right to collect such royalties.
-
-
-Section 3 -- License Conditions.
-
-Your exercise of the Licensed Rights is expressly made subject to the
-following conditions.
-
- a. Attribution.
-
- 1. If You Share the Licensed Material (including in modified
- form), You must:
-
- a. retain the following if it is supplied by the Licensor
- with the Licensed Material:
-
- i. identification of the creator(s) of the Licensed
- Material and any others designated to receive
- attribution, in any reasonable manner requested by
- the Licensor (including by pseudonym if
- designated);
-
- ii. a copyright notice;
-
- iii. a notice that refers to this Public License;
-
- iv. a notice that refers to the disclaimer of
- warranties;
-
- v. a URI or hyperlink to the Licensed Material to the
- extent reasonably practicable;
-
- b. indicate if You modified the Licensed Material and
- retain an indication of any previous modifications; and
-
- c. indicate the Licensed Material is licensed under this
- Public License, and include the text of, or the URI or
- hyperlink to, this Public License.
-
- 2. You may satisfy the conditions in Section 3(a)(1) in any
- reasonable manner based on the medium, means, and context in
- which You Share the Licensed Material. For example, it may be
- reasonable to satisfy the conditions by providing a URI or
- hyperlink to a resource that includes the required
- information.
-
- 3. If requested by the Licensor, You must remove any of the
- information required by Section 3(a)(1)(A) to the extent
- reasonably practicable.
-
- 4. If You Share Adapted Material You produce, the Adapter's
- License You apply must not prevent recipients of the Adapted
- Material from complying with this Public License.
-
-
-Section 4 -- Sui Generis Database Rights.
-
-Where the Licensed Rights include Sui Generis Database Rights that
-apply to Your use of the Licensed Material:
-
- a. for the avoidance of doubt, Section 2(a)(1) grants You the right
- to extract, reuse, reproduce, and Share all or a substantial
- portion of the contents of the database;
-
- b. if You include all or a substantial portion of the database
- contents in a database in which You have Sui Generis Database
- Rights, then the database in which You have Sui Generis Database
- Rights (but not its individual contents) is Adapted Material; and
-
- c. You must comply with the conditions in Section 3(a) if You Share
- all or a substantial portion of the contents of the database.
-
-For the avoidance of doubt, this Section 4 supplements and does not
-replace Your obligations under this Public License where the Licensed
-Rights include other Copyright and Similar Rights.
-
-
-Section 5 -- Disclaimer of Warranties and Limitation of Liability.
-
- a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
- EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
- AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
- ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
- IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
- WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
- PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
- ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
- KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
- ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
-
- b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
- TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
- NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
- INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
- COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
- USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
- ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
- DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
- IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
-
- c. The disclaimer of warranties and limitation of liability provided
- above shall be interpreted in a manner that, to the extent
- possible, most closely approximates an absolute disclaimer and
- waiver of all liability.
-
-
-Section 6 -- Term and Termination.
-
- a. This Public License applies for the term of the Copyright and
- Similar Rights licensed here. However, if You fail to comply with
- this Public License, then Your rights under this Public License
- terminate automatically.
-
- b. Where Your right to use the Licensed Material has terminated under
- Section 6(a), it reinstates:
-
- 1. automatically as of the date the violation is cured, provided
- it is cured within 30 days of Your discovery of the
- violation; or
-
- 2. upon express reinstatement by the Licensor.
-
- For the avoidance of doubt, this Section 6(b) does not affect any
- right the Licensor may have to seek remedies for Your violations
- of this Public License.
-
- c. For the avoidance of doubt, the Licensor may also offer the
- Licensed Material under separate terms or conditions or stop
- distributing the Licensed Material at any time; however, doing so
- will not terminate this Public License.
-
- d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
- License.
-
-
-Section 7 -- Other Terms and Conditions.
-
- a. The Licensor shall not be bound by any additional or different
- terms or conditions communicated by You unless expressly agreed.
-
- b. Any arrangements, understandings, or agreements regarding the
- Licensed Material not stated herein are separate from and
- independent of the terms and conditions of this Public License.
-
-
-Section 8 -- Interpretation.
-
- a. For the avoidance of doubt, this Public License does not, and
- shall not be interpreted to, reduce, limit, restrict, or impose
- conditions on any use of the Licensed Material that could lawfully
- be made without permission under this Public License.
-
- b. To the extent possible, if any provision of this Public License is
- deemed unenforceable, it shall be automatically reformed to the
- minimum extent necessary to make it enforceable. If the provision
- cannot be reformed, it shall be severed from this Public License
- without affecting the enforceability of the remaining terms and
- conditions.
-
- c. No term or condition of this Public License will be waived and no
- failure to comply consented to unless expressly agreed to by the
- Licensor.
-
- d. Nothing in this Public License constitutes or may be interpreted
- as a limitation upon, or waiver of, any privileges and immunities
- that apply to the Licensor or You, including from the legal
- processes of any jurisdiction or authority.
-
-
-=======================================================================
-
-Creative Commons is not a party to its public licenses.
-Notwithstanding, Creative Commons may elect to apply one of its public
-licenses to material it publishes and in those instances will be
-considered the "Licensor." Except for the limited purpose of indicating
-that material is shared under a Creative Commons public license or as
-otherwise permitted by the Creative Commons policies published at
-creativecommons.org/policies, Creative Commons does not authorize the
-use of the trademark "Creative Commons" or any other trademark or logo
-of Creative Commons without its prior written consent including,
-without limitation, in connection with any unauthorized modifications
-to any of its public licenses or any other arrangements,
-understandings, or agreements concerning use of licensed material. For
-the avoidance of doubt, this paragraph does not form part of the public
-licenses.
-
-Creative Commons may be contacted at creativecommons.org.
\ No newline at end of file
=====================================
gajim/data/emoticons/twemoji/twemoji.png deleted
=====================================
Binary files a/gajim/data/emoticons/twemoji/twemoji.png and /dev/null differ
=====================================
gajim/data/gui/message_actions_box.ui
=====================================
@@ -143,6 +143,7 @@
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="tooltip-text" translatable="yes">Show a list of emojis</property>
+ <property name="action-name">win.show-emoji-chooser</property>
<property name="relief">none</property>
<child>
<object class="GtkImage">
=====================================
gajim/data/gui/preferences.ui
=====================================
@@ -688,57 +688,7 @@
</packing>
</child>
<child>
- <!-- n-columns=3 n-rows=3 -->
- <object class="GtkGrid" id="emoji">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
- <property name="orientation">vertical</property>
- <property name="row-spacing">12</property>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
- <property name="label" translatable="yes">Emoji</property>
- <property name="xalign">0</property>
- <style>
- <class name="bold"/>
- </style>
- </object>
- <packing>
- <property name="left-attach">0</property>
- <property name="top-attach">0</property>
- </packing>
- </child>
- <child>
- <placeholder/>
- </child>
- <child>
- <placeholder/>
- </child>
- <child>
- <placeholder/>
- </child>
- <child>
- <placeholder/>
- </child>
- <child>
- <placeholder/>
- </child>
- <child>
- <placeholder/>
- </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>
+ <placeholder/>
</child>
</object>
</child>
=====================================
gajim/data/style/gajim.css
=====================================
@@ -748,18 +748,6 @@ #NotificationPopupTitle {
font-weight: bold;
}
-/* Emoticon Popover (ModifierChooser) */
-popover#EmoticonPopover button { background: none; border: none; box-shadow:none; padding: 0px;}
-popover#EmoticonPopover button > label { font-size: 2.0rem; }
-popover#EmoticonPopover flowboxchild > label { font-size: 2.0rem; }
-popover#EmoticonPopover flowboxchild { padding-top: 5px; padding-bottom: 5px; }
-popover#EmoticonPopover scrolledwindow { border: none; }
-popover#EmoticonPopover { padding: 5px; background-color: @theme_unfocused_base_color; }
-
-.emoji-chooser-heading { font-size: 1.15rem; font-weight: bold; padding: 5px;}
-.emoji-chooser-flowbox { padding-left: 5px; padding-right: 11px; }
-.emoji-modifier-chooser-flowbox { padding-left: 5px; }
-
/* Settings widgets ======================================================== */
/* SettingsBox */
=====================================
gajim/gtk/application.py
=====================================
@@ -244,12 +244,6 @@ def _startup(self) -> None:
idle.Monitor.set_interval(app.settings.get('autoawaytime') * 60,
app.settings.get('autoxatime') * 60)
- if sys.platform == 'darwin':
- # TODO: Remove if colored emoji rendering works well on
- # Windows and MacOS
- from gajim.gui.emoji_chooser import emoji_chooser
- emoji_chooser.load()
-
from gajim.gui_interface import Interface
self.interface = Interface()
=====================================
gajim/gtk/chat_stack.py
=====================================
@@ -712,7 +712,6 @@ def _on_function_message(self,
self._chat_control.add_info_message(message)
def _on_send_message(self) -> None:
- self._message_action_box.msg_textview.replace_emojis()
message = self._message_action_box.msg_textview.get_text()
if message.startswith('//'):
# Escape sequence for chat commands
=====================================
gajim/gtk/conversation/plain_widget.py
=====================================
@@ -14,32 +14,19 @@
from __future__ import annotations
-from typing import Any
-
import logging
-import sys
from gi.repository import Gtk
from gi.repository import Pango
from gi.repository import Gdk
-from gi.repository import GdkPixbuf
from gi.repository import GLib
-from gi.repository import GObject
-from gajim.common import app
-from gajim.common.const import StyleAttr
from gajim.common.helpers import open_uri
-from gajim.common.helpers import parse_uri
-from gajim.common.structs import URI
from gajim.common.styling import BaseHyperlink
from gajim.common.styling import PlainBlock
from gajim.common.styling import process_uris
from ..menus import get_conv_action_context_menu
-from ..menus import get_conv_uri_context_menu
-from ..emoji_data import emoji_pixbufs
-from ..emoji_data import get_emoji_pixbuf
-from ..util import get_cursor
from ..util import make_pango_attributes
log = logging.getLogger('gajim.gui.conversaion.plain_widget')
@@ -55,22 +42,11 @@ def __init__(self, account: str, selectable: bool) -> None:
self._account = account
- # We use a Gtk.Textview on MacOS, since there is no support
- # for rendering color fonts (Emojis) on MacOS yet, see:
- # https://gitlab.freedesktop.org/cairo/cairo/-/merge_requests/289
- # https://gitlab.freedesktop.org/cairo/cairo/-/merge_requests/9
- if sys.platform == 'darwin':
- if app.settings.get('dev_use_message_label'):
- self._text_widget = MessageLabel(self._account, selectable)
- else:
- self._text_widget = MessageTextview(self._account)
- else:
- self._text_widget = MessageLabel(self._account, selectable)
+ self._text_widget = MessageLabel(self._account, selectable)
self.add(self._text_widget)
def set_selectable(self, selectable: bool) -> None:
- if isinstance(self._text_widget, MessageLabel):
- self._text_widget.set_selectable(selectable)
+ self._text_widget.set_selectable(selectable)
def add_content(self, block: PlainBlock) -> None:
self._text_widget.print_text_with_styling(block)
@@ -152,295 +128,3 @@ def _on_focus_out(widget: MessageLabel,
_event: Gdk.EventFocus
) -> None:
widget.get_style_context().add_class('transparent-selection')
-
-
-class MessageTextview(Gtk.TextView):
- def __init__(self, account: str) -> None:
- Gtk.TextView.__init__(self)
- self.set_hexpand(True)
- self.set_margin_start(0)
- self.set_margin_end(0)
- self.set_border_width(0)
- self.set_left_margin(0)
- self.set_right_margin(0)
- self.set_has_tooltip(True)
- self.set_editable(False)
- self.set_cursor_visible(False)
- self.set_wrap_mode(Gtk.WrapMode.WORD_CHAR)
-
- self._handlers: dict[int, MessageTextview] = {}
-
- id_ = self.connect('query-tooltip', self._query_tooltip)
- self._handlers[id_] = self
- id_ = self.connect('button-press-event', self._on_button_press)
- self._handlers[id_] = self
- id_ = self.connect('populate-popup', self._on_populate_popup)
- self._handlers[id_] = self
-
- self._account = account
-
- # Used for changing the mouse pointer when hovering clickable URIs
- self._cursor_changed: bool = False
-
- # Keeps text selections for quoting and search actions
- self._selected_text: str = ''
-
- self.get_style_context().add_class('gajim-conversation-text')
-
- # Create Tags
- self._create_url_tags()
- self.get_buffer().create_tag('strong', weight=Pango.Weight.BOLD)
- self.get_buffer().create_tag('emphasis', style=Pango.Style.ITALIC)
- self.get_buffer().create_tag('strike', strikethrough=True)
- self.get_buffer().create_tag('pre', family='monospace')
-
- self.connect('destroy', self._on_destroy)
-
- def _on_destroy(self, *args: Any) -> None:
- for id_, widget in self._handlers.items():
- if widget.handler_is_connected(id_):
- widget.disconnect(id_)
- self._handlers.clear()
-
- def _create_url_tags(self) -> None:
- color = app.css_config.get_value('.gajim-url', StyleAttr.COLOR)
- for name in URI_TAGS:
- tag = self.get_buffer().create_tag(name,
- foreground=color,
- underline=Pango.Underline.SINGLE)
- tag.connect('event', self._on_uri_clicked, tag)
-
- def clear(self) -> None:
- buffer_ = self.get_buffer()
- start, end = buffer_.get_bounds()
- buffer_.delete(start, end)
-
- def get_text(self) -> str:
- buffer_ = self.get_buffer()
- start, end = buffer_.get_bounds()
- return buffer_.get_text(start, end, False)
-
- def print_text_with_styling(self, block: PlainBlock) -> None:
- buffer_ = self.get_buffer()
- buffer_.insert(buffer_.get_start_iter(), block.text.strip())
-
- if len(self.get_text()) > 10000:
- # Limit message styling processing
- return
-
- for span in block.spans:
- start_iter = buffer_.get_iter_at_offset(span.start)
- end_iter = buffer_.get_iter_at_offset(span.end)
- buffer_.apply_tag_by_name(span.name, start_iter, end_iter)
-
- for uri in block.uris:
- start_iter = buffer_.get_iter_at_offset(uri.start)
- end_iter = buffer_.get_iter_at_offset(uri.end)
- buffer_.apply_tag_by_name(uri.name, start_iter, end_iter)
-
- for emoji in block.emojis:
- start_iter = buffer_.get_iter_at_offset(emoji.start)
- end_iter = buffer_.get_iter_at_offset(emoji.end)
- if emoji_pixbufs.complete:
- # Only search for pixbuf if loading is completed
- pixbuf = get_emoji_pixbuf(emoji.text)
- if pixbuf is None:
- buffer_.insert(end_iter, emoji.text)
- else:
- pixbuf = pixbuf.copy()
- anchor = buffer_.create_child_anchor(end_iter)
- anchor.plaintext = emoji.text # type: ignore
- img = Gtk.Image.new_from_pixbuf(pixbuf)
- img.show()
- self.add_child_at_anchor(img, anchor)
- buffer_.delete(
- buffer_.get_iter_at_offset(emoji.start),
- buffer_.get_iter_at_offset(emoji.end))
- else:
- # Set marks and save them so we can replace emojis
- # once loading is complete
- start_mark = buffer_.create_mark(None, end_iter, True)
- buffer_.insert(end_iter, emoji.text)
- end_mark = buffer_.create_mark(None, end_iter, True)
- emoji_pixbufs.append_marks(
- self, start_mark, end_mark, emoji.text)
-
- def replace_emoji(self,
- start_mark: Gtk.TextMark,
- end_mark: Gtk.TextMark,
- pixbuf: GdkPixbuf.Pixbuf,
- codepoint: str
- ) -> None:
- buffer_ = self.get_buffer()
- start_iter = buffer_.get_iter_at_mark(start_mark)
- end_iter = buffer_.get_iter_at_mark(end_mark)
- buffer_.delete(start_iter, end_iter)
-
- anchor = buffer_.create_child_anchor(start_iter)
- anchor.plaintext = codepoint # type: ignore
- img = Gtk.Image.new_from_pixbuf(pixbuf)
- img.show()
- self.add_child_at_anchor(img, anchor)
- buffer_.delete_mark(start_mark)
- buffer_.delete_mark(end_mark)
-
- def add_action_phrase(self, text: str, nickname: str) -> None:
- text = text.replace('/me', f'* {nickname}', 1)
-
- buffer_ = self.get_buffer()
- buffer_.insert(buffer_.get_start_iter(), text.strip())
-
- uris = process_uris(text)
- for uri in uris:
- start_iter = buffer_.get_iter_at_offset(uri.start)
- end_iter = buffer_.get_iter_at_offset(uri.end)
- buffer_.apply_tag_by_name(uri.name, start_iter, end_iter)
-
- start_iter = buffer_.get_start_iter()
- end_iter = buffer_.get_end_iter()
- buffer_.apply_tag_by_name('emphasis', start_iter, end_iter)
-
- def _query_tooltip(self,
- widget: Gtk.TextView,
- x_pos: int,
- y_pos: int,
- _keyboard_mode: bool,
- _tooltip: Gtk.Tooltip
- ) -> bool:
-
- window = widget.get_window(Gtk.TextWindowType.TEXT)
- assert window is not None
- x_pos, y_pos = self.window_to_buffer_coords(
- Gtk.TextWindowType.TEXT, x_pos, y_pos)
-
- iter_ = self.get_iter_at_position(x_pos, y_pos)[1]
- for tag in iter_.get_tags():
- tag_name = tag.get_property('name')
- if tag_name in URI_TAGS:
- window.set_cursor(get_cursor('pointer'))
- self._cursor_changed = True
- return False
-
- if self._cursor_changed:
- window.set_cursor(get_cursor('text'))
- self._cursor_changed = False
- return False
-
- def _on_button_press(self, _widget: Any, event: Gdk.EventButton) -> bool:
- '''
- We don’t open the standard context menu when receiving
- a click on tagged text.
- If it’s untagged text, check if something is selected
- '''
- self._selected_text = ''
-
- if event.button != 3:
- # If it’s not a right click
- return False
-
- x_pos, y_pos = self.window_to_buffer_coords(
- Gtk.TextWindowType.TEXT,
- int(event.x),
- int(event.y))
- _, iter_ = self.get_iter_at_location(x_pos, y_pos)
- tags = iter_.get_tags()
-
- if tags:
- # A tagged text fragment has been clicked
- for tag in tags:
- if tag.get_property('name') in URI_TAGS:
- # Block regular context menu
- return True
-
- # Check if there is a selection and make it available for
- # _on_populate_popup
- buffer_ = self.get_buffer()
- return_val = buffer_.get_selection_bounds()
- if return_val:
- # Something has been selected, get the text
- start_sel, finish_sel = return_val[0], return_val[1]
- self._selected_text = buffer_.get_text(
- start_sel, finish_sel, True)
- elif iter_.get_char() and ord(iter_.get_char()) > 31:
- # Clicked on a word, take whole word for selection
- start_sel = iter_.copy()
- if not start_sel.starts_word():
- start_sel.backward_word_start()
- finish_sel = iter_.copy()
- if not finish_sel.ends_word():
- finish_sel.forward_word_end()
- self._selected_text = buffer_.get_text(
- start_sel, finish_sel, True)
- return False
-
- def _on_populate_popup(self,
- _textview: Gtk.TextView,
- menu: Gtk.Menu
- ) -> None:
- '''
- Overrides the default context menu.
- If text is selected, a submenu with actions on the selection is added.
- (see _on_button_press)
- '''
- if not self._selected_text:
- menu.show_all()
- return
-
- action_menu_item = get_conv_action_context_menu(
- self._account, self._selected_text)
- menu.prepend(action_menu_item)
- menu.show_all()
-
- def _on_uri_clicked(self,
- texttag: Gtk.TextTag,
- _widget: Any,
- event: Gdk.Event,
- iter_: Gtk.TextIter,
- _kind: Gtk.TextTag
- ) -> int:
- if event.type != Gdk.EventType.BUTTON_PRESS:
- return Gdk.EVENT_PROPAGATE
-
- begin_iter = iter_.copy()
- # we get the beginning of the tag
- while not begin_iter.starts_tag(texttag):
- begin_iter.backward_char()
- end_iter = iter_.copy()
- # we get the end of the tag
- while not end_iter.ends_tag(texttag):
- end_iter.forward_char()
-
- # Detect XHTML-IM link
- word = getattr(texttag, 'href', None)
- if not word:
- word = self.get_buffer().get_text(begin_iter, end_iter, True)
-
- uri = parse_uri(word)
- if event.button.button == 3: # right click
- self._show_uri_context_menu(uri)
- return Gdk.EVENT_STOP
-
- # TODO:
- # self.plugin_modified = False
- # app.plugin_manager.extension_point(
- # 'hyperlink_handler', uri, self, self.get_toplevel())
- # if self.plugin_modified:
- # return Gdk.EVENT_STOP
-
- open_uri(uri, account=self._account)
- return Gdk.EVENT_STOP
-
- def _show_uri_context_menu(self, uri: URI) -> None:
- menu = get_conv_uri_context_menu(self._account, uri)
- if menu is None:
- log.warning('No handler for URI type: %s', uri)
- return
-
- def _destroy(menu: Gtk.Menu, _pspec: GObject.ParamSpec) -> None:
- visible = menu.get_property('visible')
- if not visible:
- GLib.idle_add(menu.destroy)
-
- menu.attach_to_widget(self, None)
- menu.connect('notify::visible', _destroy)
- menu.popup_at_pointer()
=====================================
gajim/gtk/emoji_chooser.py deleted
=====================================
@@ -1,412 +0,0 @@
-# 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/>.
-
-import logging
-import weakref
-from collections import OrderedDict
-
-from gi.repository import Gtk
-from gi.repository import GLib
-from gi.repository import GdkPixbuf
-from gi.repository import Pango
-
-from gajim.common import app
-from gajim.common import helpers
-from gajim.common import configpaths
-
-from .builder import get_builder
-from .emoji_data import emoji_data
-from .emoji_data import emoji_pixbufs
-from .emoji_data import Emoji
-
-MODIFIER_MAX_CHILDREN_PER_LINE = 6
-MAX_CHILDREN_PER_LINE = 10
-
-log = logging.getLogger('gajim.emoji')
-
-
-class Section(Gtk.Box):
- def __init__(self, name, search_entry, press_cb, chooser):
- Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL)
- self._chooser = chooser
- self._press_cb = press_cb
- self.pixbuf_generator = None
- self.heading = Gtk.Label(label=name)
- self.heading.set_halign(Gtk.Align.START)
- self.heading.get_style_context().add_class('emoji-chooser-heading')
- self.add(self.heading)
-
- self.flowbox = Gtk.FlowBox()
- self.flowbox.get_style_context().add_class('emoji-chooser-flowbox')
- self.flowbox.set_max_children_per_line(MAX_CHILDREN_PER_LINE)
- self.flowbox.set_filter_func(self._filter_func, search_entry)
- self.flowbox.connect('child-activated', press_cb)
-
- self.add(self.flowbox)
- self.show_all()
-
- def _filter_func(self, child, search_entry):
- name = search_entry.get_text()
- if not name:
- self.show()
- return True
-
- if name in child.desc:
- self.show()
- return True
- return False
-
- def add_emoji(self, codepoint, attrs):
- # Return always True, this method is used for the emoji factory
- # called by GLib.idle_add()
- pixbuf = self._get_next_pixbuf()
-
- variations = attrs.get('variations', None)
- if variations is None:
- if pixbuf is None:
- return True
- self.flowbox.add(EmojiChild(codepoint, pixbuf, attrs['desc']))
- if pixbuf != 'font':
- # We save the pixbuf for fast access if we need to
- # replace a codepoint in the textview
- emoji_pixbufs[codepoint] = pixbuf
- else:
- if pixbuf is not None:
- chooser = ModifierChooser()
-
- # Iterate over the variations and add the codepoints
- for codepoint_ in variations.keys():
- pixbuf_ = self._get_next_pixbuf()
- if pixbuf_ is None:
- continue
-
- if pixbuf_ == 'font':
- if not self._chooser.font_supports_codepoint(
- codepoint_):
- continue
- else:
- emoji_pixbufs[codepoint_] = pixbuf_
-
- # Only codepoints are added which the
- # font or theme supports
- chooser.add_emoji(codepoint_, pixbuf_)
-
- # Check if we successfully added codepoints with modifiers
- if chooser.has_child:
- # If we have children then add a button
- # and set the popover
- child = EmojiModifierChild(
- codepoint, pixbuf, attrs['desc'])
- child.button.set_popover(chooser)
- chooser.flowbox.connect(
- 'child-activated', self._press_cb)
- else:
- # If no children were added, destroy the chooser
- # and add a EmojiChild instead of a EmojiModifierChild
- chooser.destroy()
- child = EmojiChild(codepoint, pixbuf, attrs['desc'])
-
- if pixbuf != 'font':
- emoji_pixbufs[codepoint] = pixbuf
-
- self.flowbox.add(child)
- else:
- # We don’t have a image for the base codepoint
- # so skip all modifiers of it
- for _entry in variations:
- pixbuf = self._get_next_pixbuf()
- return True
-
- def clear_emojis(self):
- def _remove_emoji(emoji):
- self.flowbox.remove(emoji)
- emoji.destroy()
- self.flowbox.foreach(_remove_emoji)
-
- def _get_next_pixbuf(self):
- if self.pixbuf_generator is None:
- return 'font'
- return next(self.pixbuf_generator, False)
-
-
-class EmojiChild(Gtk.FlowBoxChild):
- def __init__(self, codepoint, pixbuf, desc):
- Gtk.FlowBoxChild.__init__(self)
- self.desc = desc
- self.codepoint = codepoint
- self.pixbuf = pixbuf
- if pixbuf == 'font':
- self.add(Gtk.Label(label=codepoint))
- else:
- self.add(Gtk.Image.new_from_pixbuf(pixbuf))
- self.set_tooltip_text(desc)
- self.show_all()
-
- def get_emoji(self):
- if self.pixbuf != 'font':
- pixbuf = self.get_child().get_pixbuf()
- pixbuf = pixbuf.scale_simple(Emoji.INPUT_SIZE,
- Emoji.INPUT_SIZE,
- GdkPixbuf.InterpType.HYPER)
- return self.codepoint, pixbuf
- return self.codepoint, None
-
-
-class EmojiModifierChild(Gtk.FlowBoxChild):
- def __init__(self, codepoint, pixbuf, desc):
- Gtk.FlowBoxChild.__init__(self)
- self.desc = desc
- self.codepoint = codepoint
- self.pixbuf = pixbuf
-
- self.button = Gtk.MenuButton()
- self.button.set_relief(Gtk.ReliefStyle.NONE)
- self.button.connect('button-press-event', self._button_press)
-
- if pixbuf == 'font':
- self.button.remove(self.button.get_child())
- label = Gtk.Label(label=codepoint)
- self.button.add(label)
- else:
- self.button.get_child().set_from_pixbuf(pixbuf)
-
- self.set_tooltip_text(desc)
- self.add(self.button)
- self.show_all()
-
- def _button_press(self, button, event):
- if event.button == 3:
- button.get_popover().show()
- button.get_popover().get_child().unselect_all()
- return True
- if event.button == 1:
- self.get_parent().emit('child-activated', self)
- return True
- return False
-
- def get_emoji(self):
- if self.pixbuf != 'font':
- pixbuf = self.button.get_child().get_pixbuf()
- pixbuf = pixbuf.scale_simple(Emoji.INPUT_SIZE,
- Emoji.INPUT_SIZE,
- GdkPixbuf.InterpType.HYPER)
- return self.codepoint, pixbuf
- return self.codepoint, None
-
-
-class ModifierChooser(Gtk.Popover):
- def __init__(self):
- Gtk.Popover.__init__(self)
- self.set_name('EmoticonPopover')
- self._has_child = False
-
- self.flowbox = Gtk.FlowBox()
- self.flowbox.get_style_context().add_class(
- 'emoji-modifier-chooser-flowbox')
- self.flowbox.set_size_request(200, -1)
- self.flowbox.set_max_children_per_line(MODIFIER_MAX_CHILDREN_PER_LINE)
- self.flowbox.show()
- self.add(self.flowbox)
-
- @property
- def has_child(self):
- return self._has_child
-
- def add_emoji(self, codepoint, pixbuf):
- self.flowbox.add(EmojiChild(codepoint, pixbuf, None))
- self._has_child = True
-
-
-class EmojiChooser(Gtk.Popover):
-
- _section_names = [
- 'Smileys & People',
- 'Animals & Nature',
- 'Food & Drink',
- 'Travel & Places',
- 'Activities',
- 'Objects',
- 'Symbols',
- 'Flags'
- ]
-
- def __init__(self):
- super().__init__()
- self.set_name('EmoticonPopover')
- self._text_widget = None
- self._load_source_id = None
- self._pango_layout = Pango.Layout(self.get_pango_context())
-
- self._builder = get_builder('emoji_chooser.ui')
- self._search = self._builder.get_object('search')
- self._stack = self._builder.get_object('stack')
-
- self._sections = OrderedDict()
- for name in self._section_names:
- self._sections[name] = Section(
- name, self._search, self._on_emoticon_press, self)
-
- section_box = self._builder.get_object('section_box')
- for section in self._sections.values():
- section_box.add(section)
-
- self.add(self._builder.get_object('box'))
-
- self.connect('key-press-event', self._key_press)
- self._builder.connect_signals(self)
-
- @property
- def text_widget(self):
- return self._text_widget
-
- @text_widget.setter
- def text_widget(self, value):
- # Hold only a weak reference so if we can destroy
- # the ChatControl
- self._text_widget = weakref.ref(value)
-
- def _key_press(self, _widget, event):
- return self._search.handle_event(event)
-
- def _search_changed(self, _entry):
- for section in self._sections.values():
- section.hide()
- section.flowbox.invalidate_filter()
- self._switch_stack()
-
- def _clear_sections(self):
- for section in self._sections.values():
- section.clear_emojis()
-
- def _switch_stack(self):
- for section in self._sections.values():
- if section.is_visible():
- self._stack.set_visible_child_name('list')
- return
- self._stack.set_visible_child_name('not-found')
-
- @staticmethod
- def _get_current_theme():
- theme = app.settings.get('emoticons_theme')
- themes = helpers.get_available_emoticon_themes()
- if theme not in themes:
- app.settings.set('emoticons_theme', 'noto')
- theme = 'noto'
- return theme
-
- @staticmethod
- def _get_emoji_theme_path(theme):
- if theme == 'font':
- return 'font'
-
- base_path = configpaths.get('EMOTICONS')
- emoticons_data_path = base_path / theme / f'{theme}.png'
- if emoticons_data_path.exists():
- return emoticons_data_path
-
- emoticons_user_path = configpaths.get('MY_EMOTS') / f'{theme}.png'
- if emoticons_user_path.exists():
- return emoticons_user_path
-
- log.warning('Could not find emoji theme: %s', theme)
- return None
-
- def load(self):
- theme = self._get_current_theme()
- path = self._get_emoji_theme_path(theme)
- if not theme or path is None:
- self._clear_sections()
- emoji_pixbufs.clear()
- return
-
- # Attach pixbuf generator
- pixbuf_generator = None
- if theme != 'font':
- pixbuf_generator = self._get_next_pixbuf(path)
- for section in self._sections.values():
- section.pixbuf_generator = pixbuf_generator
-
- if self._load_source_id is not None:
- GLib.source_remove(self._load_source_id)
-
- # When we change emoji theme
- self._clear_sections()
- emoji_pixbufs.clear()
-
- factory = self._emoji_factory(theme == 'font')
- self._load_source_id = GLib.idle_add(lambda: next(factory, False),
- priority=GLib.PRIORITY_LOW)
-
- def _emoji_factory(self, font):
- for codepoint, attrs in emoji_data.items():
- if not attrs['fully-qualified']:
- # We don’t add these to the UI
- continue
-
- if font and not self.font_supports_codepoint(codepoint):
- continue
-
- section = self._sections[attrs['group']]
- yield section.add_emoji(codepoint, attrs)
- self._load_source_id = None
- emoji_pixbufs.complete = True
-
- def font_supports_codepoint(self, codepoint):
- self._pango_layout.set_text(codepoint, -1)
- if self._pango_layout.get_unknown_glyphs_count():
- return False
- if len(codepoint) > 1:
- # The font supports each of the codepoints
- # Check if the rendered glyph is more than one char
- if self._pango_layout.get_size()[0] > 19000:
- return False
- return True
-
- @staticmethod
- def _get_next_pixbuf(path):
- src_x = src_y = cur_column = 0
- atlas = GdkPixbuf.Pixbuf.new_from_file(str(path))
-
- while True:
- src_x = cur_column * Emoji.PARSE_WIDTH
- subpixbuf = atlas.new_subpixbuf(src_x, src_y,
- Emoji.PARSE_WIDTH,
- Emoji.PARSE_HEIGHT)
-
- if list(subpixbuf.get_pixels())[0:4] == [0, 0, 0, 255]:
- # top left corner is a black pixel means image is missing
- subpixbuf = None
-
- if cur_column == Emoji.PARSE_COLUMNS - 1:
- src_y += Emoji.PARSE_WIDTH
- cur_column = 0
- else:
- cur_column += 1
-
- yield subpixbuf
-
- def _on_emoticon_press(self, flowbox, child):
- GLib.timeout_add(100, flowbox.unselect_child, child)
- codepoint, pixbuf = child.get_emoji()
- self._text_widget().insert_emoji(codepoint, pixbuf)
- self._text_widget().grab_focus()
-
- def do_destroy(self):
- # Remove the references we hold to other objects
- self._text_widget = None
- # Never destroy, creating a new EmoticonPopover is expensive
- return True
-
-
-emoji_chooser = EmojiChooser()
=====================================
gajim/gtk/emoji_data.py deleted
=====================================
The diff for this file was not included because it is too large.
=====================================
gajim/gtk/emoji_data.pyi deleted
=====================================
@@ -1,44 +0,0 @@
-# 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 enum import IntEnum
-from collections import OrderedDict
-
-from gi.repository import GdkPixbuf
-from gi.repository import Gtk
-
-class Emoji(IntEnum):
- ...
-
-def is_emoji(codepoints: str) -> bool: ...
-def get_emoji_pixbuf(codepoints: str) -> GdkPixbuf.Pixbuf: ...
-
-class EmojiData(OrderedDict):
- def get_regex(self) -> str: ...
-
-class EmojiPixbufs(dict):
- @property
- def complete(self) -> bool: ...
- @complete.setter
- def complete(self, value: bool) -> None: ...
- def clear(self) -> None: ...
- def append_marks(self,
- textview: Gtk.TextView,
- start: Gtk.TextMark,
- end: Gtk.TextMark,
- codepoint: str) -> None: ...
-
-emoji_pixbufs: EmojiPixbufs
-emoji_ascii_data: dict[str, str]
-emoji_data: EmojiData
=====================================
gajim/gtk/message_actions_box.py
=====================================
@@ -18,7 +18,6 @@
from typing import Optional
import os
-import sys
import tempfile
import logging
import uuid
@@ -45,7 +44,6 @@
from .builder import get_builder
from .dialogs import ErrorDialog
-from .emoji_chooser import emoji_chooser
from .menus import get_encryption_menu
from .menus import get_format_menu
from .menus import get_groupchat_menu
@@ -98,8 +96,6 @@ def __init__(self) -> None:
self._ui.quick_invite_button.set_action_name('win.muc-invite')
- self._init_emoticon_popover()
-
self.show_all()
self._ui.connect_signals(self)
@@ -157,7 +153,7 @@ def _on_action(self,
self._on_format(action_name)
elif action_name == 'show-emoji-chooser':
- self._on_show_emoji_chooser()
+ self.msg_textview.emit('insert-emoji')
elif action_name == 'quote':
assert param
@@ -389,31 +385,6 @@ def _set_settings_menu(self, contact: ChatContactT) -> None:
menu = get_singlechat_menu(contact)
self._ui.settings_menu.set_menu_model(menu)
- def _init_emoticon_popover(self) -> None:
- if not app.settings.get('emoticons_theme'):
- return
-
- if sys.platform == 'darwin':
- emoji_chooser.text_widget = self.msg_textview
- self._ui.emoticons_button.set_popover(emoji_chooser)
- return
-
- self._ui.emoticons_button.set_sensitive(True)
- self._ui.emoticons_button.connect('clicked',
- self._on_emoticon_button_clicked)
-
- def toggle_emoticons(self) -> None:
- if app.settings.get('emoticons_theme'):
- self._ui.emoticons_button.set_no_show_all(False)
- self._ui.emoticons_button.show()
- else:
- self._ui.emoticons_button.set_no_show_all(True)
- self._ui.emoticons_button.hide()
-
- def _on_emoticon_button_clicked(self, _widget: Gtk.Button) -> None:
- self.msg_textview.emit('insert-emoji')
- self._ui.emoticons_button.set_active(False)
-
def _on_format(self, name: str) -> None:
name = name.removeprefix('input-')
self.msg_textview.apply_formatting(name)
@@ -421,14 +392,6 @@ def _on_format(self, name: str) -> None:
def _on_clear(self) -> None:
self.msg_textview.clear()
- def _on_show_emoji_chooser(self) -> None:
- if sys.platform == 'darwin':
- popover = self._ui.emoticons_button.get_popover()
- assert popover
- popover.show()
- else:
- self.msg_textview.emit('insert-emoji')
-
def _on_send_file_enabled_changed(self,
action: Gio.SimpleAction,
_param: GObject.ParamSpec) -> None:
=====================================
gajim/gtk/message_input.py
=====================================
@@ -25,11 +25,9 @@
from collections import defaultdict
import logging
-import sys
from gi.repository import Gtk
from gi.repository import Gdk
-from gi.repository import GdkPixbuf
from gi.repository import GLib
from gi.repository import Pango
@@ -420,60 +418,6 @@ def apply_formatting(self, formatting: str) -> None:
buf.get_iter_at_offset(start_offset),
buf.get_iter_at_offset(end_offset + 2))
- def replace_emojis(self) -> None:
- if sys.platform != 'darwin':
- # TODO: Remove if colored emoji rendering works well on MacOS
- return
-
- theme = app.settings.get('emoticons_theme')
- if not theme or theme == 'font':
- return
-
- def _replace(anchor: Gtk.TextChildAnchor) -> None:
- if anchor is None:
- return
- image = anchor.get_widgets()[0]
- if hasattr(image, 'codepoint'):
- # found emoji
- codepoint = getattr(image, 'codepoint')
- self._replace_char_at_iter(iter_, codepoint)
- image.destroy()
-
- iter_ = self.get_buffer().get_start_iter()
- _replace(iter_.get_child_anchor())
-
- while iter_.forward_char():
- _replace(iter_.get_child_anchor())
-
- def _replace_char_at_iter(self,
- iter_: Gtk.TextIter,
- new_char: str
- ) -> None:
- buf = self.get_buffer()
- iter_2 = iter_.copy()
- iter_2.forward_char()
- buf.delete(iter_, iter_2)
- buf.insert(iter_, new_char)
-
- def insert_emoji(self, codepoint: str, pixbuf: GdkPixbuf.Pixbuf) -> None:
- buf = self.get_buffer()
- if buf.get_char_count():
- # buffer contains text
- buf.insert_at_cursor(' ')
-
- insert_mark = buf.get_insert()
- insert_iter = buf.get_iter_at_mark(insert_mark)
-
- if pixbuf is None:
- buf.insert(insert_iter, codepoint)
- else:
- anchor = buf.create_child_anchor(insert_iter)
- image = Gtk.Image.new_from_pixbuf(pixbuf)
- setattr(image, 'codepoint', codepoint)
- image.show()
- self.add_child_at_anchor(image, anchor)
- buf.insert_at_cursor(' ')
-
def clear(self, *args: Any) -> None:
buf = self.get_buffer()
start, end = buf.get_bounds()
=====================================
gajim/gtk/preferences.py
=====================================
@@ -26,7 +26,6 @@
from gajim.common import app
from gajim.common import configpaths
-from gajim.common import helpers
from gajim.common.const import THRESHOLD_OPTIONS
from gajim.common.i18n import _
from gajim.common.helpers import open_file
@@ -42,7 +41,6 @@
from .const import SettingType
from .dialogs import DialogButton
from .dialogs import ConfirmationDialog
-from .emoji_chooser import emoji_chooser
from .settings import PopoverSetting
from .settings import SettingsBox
from .settings import SettingsDialog
@@ -81,8 +79,6 @@ def __init__(self) -> None:
self.add(self._ui.grid)
- self._check_emoji_theme()
-
prefs: list[tuple[str, Type[PreferenceBox]]] = [
('window_behaviour', WindowBehaviour),
('plugins', Plugins),
@@ -95,7 +91,6 @@ def __init__(self) -> None:
('status_message', StatusMessage),
('automatic_status', AutomaticStatus),
('themes', Themes),
- ('emoji', Emoji),
('server', Server),
('audio', Audio),
('video', Video),
@@ -117,10 +112,6 @@ def __init__(self) -> None:
self._ui.connect_signals(self)
self.show_all()
- if sys.platform != 'darwin':
- # TODO: Remove if colored emoji rendering works well on
- # Windows and MacOS
- self._ui.emoji.hide()
def get_ui(self):
return self._ui
@@ -157,14 +148,6 @@ def update_proxy_list(self) -> None:
miscellaneous = cast(Miscellaneous, self._prefs['miscellaneous'])
miscellaneous.update_proxy_list()
- @staticmethod
- def _check_emoji_theme() -> None:
- # Ensure selected emoji theme is valid
- emoji_themes = helpers.get_available_emoticon_themes()
- settings_theme = app.settings.get('emoticons_theme')
- if settings_theme not in emoji_themes:
- app.settings.set('emoticons_theme', 'font')
-
def _on_destroy(self, _widget: Gtk.Widget) -> None:
self._prefs.clear()
app.check_finalize(self)
@@ -704,42 +687,6 @@ def _on_dark_theme(value: str, *args: Any) -> None:
app.ged.raise_event(StyleChanged())
-class Emoji(PreferenceBox):
- def __init__(self, *args: Any) -> None:
- if sys.platform != 'darwin':
- # TODO: Remove if colored emoji rendering works well on
- # Windows and MacOS
- PreferenceBox.__init__(self, [])
- return
-
- emoji_themes_items: list[str] = []
- for theme in helpers.get_available_emoticon_themes():
- emoji_themes_items.append(theme)
-
- settings = [
- Setting(SettingKind.POPOVER,
- _('Emoji Theme'),
- SettingType.CONFIG,
- 'emoticons_theme',
- desc=_('Choose from various emoji styles'),
- props={'entries': emoji_themes_items},
- callback=self._on_emoticons_theme)
- ]
-
- PreferenceBox.__init__(self, settings)
-
- def _on_emoticons_theme(self, *args: Any) -> None:
- emoji_chooser.load()
- self._toggle_emoticons()
-
- @staticmethod
- def _toggle_emoticons() -> None:
- # TODO
- # for ctrl in app.window.get_controls():
- # ctrl.toggle_emoticons()
- pass
-
-
class Server(PreferenceBox):
def __init__(self, *args: Any) -> None:
=====================================
pyproject.toml
=====================================
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
[tool.codespell]
-skip = "*__pycache__*,gajim/gui,gajim/data/icons,gajim/data/sounds,gajim/data/emoticons"
+skip = "*__pycache__*,gajim/gui,gajim/data/icons,gajim/data/sounds"
ignore-words-list = "claus,pres,ser,trough"
=====================================
setup.cfg
=====================================
@@ -49,8 +49,6 @@ install_requires =
[options.package_data]
gajim =
- data/emoticons/*/*.png
- data/emoticons/*/LICENSE
data/gui/*.ui
data/icons/hicolor/*/*/*.png
data/icons/hicolor/*/*/*.svg
View it on GitLab: https://dev.gajim.org/gajim/gajim/-/commit/3dfcd112926ce812fbb7351e663565b9…
--
View it on GitLab: https://dev.gajim.org/gajim/gajim/-/commit/3dfcd112926ce812fbb7351e663565b9…
You're receiving this email because of your account on dev.gajim.org.