Philipp Hörist pushed to branch master at gajim / gajim
Commits:
-
1f22acaa
by lovetox at 2021-12-18T00:42:36+01:00
-
fa918de2
by lovetox at 2021-12-18T00:42:36+01:00
-
489659df
by lovetox at 2021-12-18T00:42:36+01:00
5 changed files:
- + gajim/gtk/builder.py
- + gajim/gtk/builder.pyi
- gajim/gtk/conversation/code_widget.py
- gajim/gtk/search_view.py
- gajim/gtk/util.py
Changes:
| 1 |
+# This file is part of Gajim.
|
|
| 2 |
+#
|
|
| 3 |
+# Gajim is free software; you can redistribute it and/or modify
|
|
| 4 |
+# it under the terms of the GNU General Public License as published
|
|
| 5 |
+# by the Free Software Foundation; version 3 only.
|
|
| 6 |
+#
|
|
| 7 |
+# Gajim is distributed in the hope that it will be useful,
|
|
| 8 |
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
| 9 |
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
| 10 |
+# GNU General Public License for more details.
|
|
| 11 |
+#
|
|
| 12 |
+# You should have received a copy of the GNU General Public License
|
|
| 13 |
+# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
|
| 14 |
+ |
|
| 15 |
+from typing import Any
|
|
| 16 |
+from typing import Optional
|
|
| 17 |
+ |
|
| 18 |
+import sys
|
|
| 19 |
+import functools
|
|
| 20 |
+import xml.etree.ElementTree as ET
|
|
| 21 |
+ |
|
| 22 |
+from gi.repository import Gtk
|
|
| 23 |
+from gi.repository import Gio
|
|
| 24 |
+ |
|
| 25 |
+from gajim.common import i18n
|
|
| 26 |
+from gajim.common.i18n import _
|
|
| 27 |
+from gajim.common import configpaths
|
|
| 28 |
+ |
|
| 29 |
+ |
|
| 30 |
+class Builder:
|
|
| 31 |
+ |
|
| 32 |
+ filename = ''
|
|
| 33 |
+ |
|
| 34 |
+ def __init__(self,
|
|
| 35 |
+ filename: Optional[str] = None,
|
|
| 36 |
+ widgets: Optional[list[str]] = None,
|
|
| 37 |
+ domain: Optional[str] = None,
|
|
| 38 |
+ gettext_: Optional[Any] = None) -> None:
|
|
| 39 |
+ |
|
| 40 |
+ if filename is None:
|
|
| 41 |
+ filename = self.filename
|
|
| 42 |
+ |
|
| 43 |
+ self._builder = Gtk.Builder()
|
|
| 44 |
+ |
|
| 45 |
+ if domain is None:
|
|
| 46 |
+ domain = i18n.DOMAIN
|
|
| 47 |
+ self._builder.set_translation_domain(domain)
|
|
| 48 |
+ |
|
| 49 |
+ if gettext_ is None:
|
|
| 50 |
+ gettext_ = _
|
|
| 51 |
+ |
|
| 52 |
+ xml_text = self._load_string_from_filename(filename, gettext_)
|
|
| 53 |
+ |
|
| 54 |
+ if widgets is not None:
|
|
| 55 |
+ self._builder.add_objects_from_string(xml_text, widgets)
|
|
| 56 |
+ else:
|
|
| 57 |
+ self._builder.add_from_string(xml_text)
|
|
| 58 |
+ |
|
| 59 |
+ @staticmethod
|
|
| 60 |
+ @functools.lru_cache(maxsize=None)
|
|
| 61 |
+ def _load_string_from_filename(filename: str, gettext_: Any) -> str:
|
|
| 62 |
+ file_path = str(configpaths.get('GUI') / filename)
|
|
| 63 |
+ |
|
| 64 |
+ if sys.platform == "win32":
|
|
| 65 |
+ # This is a workaround for non working translation on Windows
|
|
| 66 |
+ tree = ET.parse(file_path)
|
|
| 67 |
+ for node in tree.iter():
|
|
| 68 |
+ if 'translatable' in node.attrib and node.text is not None:
|
|
| 69 |
+ node.text = gettext_(node.text)
|
|
| 70 |
+ |
|
| 71 |
+ return ET.tostring(tree.getroot(),
|
|
| 72 |
+ encoding='unicode',
|
|
| 73 |
+ method='xml')
|
|
| 74 |
+ |
|
| 75 |
+ file = Gio.File.new_for_path(file_path)
|
|
| 76 |
+ content = file.load_contents(None)
|
|
| 77 |
+ return content[1].decode()
|
|
| 78 |
+ |
|
| 79 |
+ def __getattr__(self, name: str) -> Any:
|
|
| 80 |
+ try:
|
|
| 81 |
+ return getattr(self._builder, name)
|
|
| 82 |
+ except AttributeError:
|
|
| 83 |
+ return self._builder.get_object(name)
|
|
| 84 |
+ |
|
| 85 |
+ def get(self, name: str) -> Any:
|
|
| 86 |
+ return self._builder.get_object(name)
|
|
| 87 |
+ |
|
| 88 |
+ |
|
| 89 |
+class SearchViewBuilder(Builder):
|
|
| 90 |
+ filename = 'search_view.ui'
|
| 1 |
+ |
|
| 2 |
+from typing import Any
|
|
| 3 |
+from typing import Literal
|
|
| 4 |
+from typing import overload
|
|
| 5 |
+ |
|
| 6 |
+from gi.repository import Gtk
|
|
| 7 |
+ |
|
| 8 |
+ |
|
| 9 |
+ |
|
| 10 |
+class Builder: ...
|
|
| 11 |
+ |
|
| 12 |
+ |
|
| 13 |
+class SearchViewBuilder(Builder):
|
|
| 14 |
+ |
|
| 15 |
+ filename: str = ...
|
|
| 16 |
+ |
|
| 17 |
+ @overload
|
|
| 18 |
+ def get(self, name: Literal['search_entry']) -> Gtk.Entry: ...
|
|
| 19 |
+ @overload
|
|
| 20 |
+ def get(self, name: Literal['date_hint']) -> Gtk.Label: ...
|
|
| 21 |
+ @overload
|
|
| 22 |
+ def get(self, name: Literal['search_checkbutton']) -> Gtk.CheckButton: ...
|
|
| 23 |
+ @overload
|
|
| 24 |
+ def get(self, name: Literal['header_box']) -> Gtk.ListBox: ...
|
|
| 25 |
+ @overload
|
|
| 26 |
+ def get(self, name: Literal['header_name_label']) -> Gtk.Label: ...
|
|
| 27 |
+ @overload
|
|
| 28 |
+ def get(self, name: Literal['header_date_label']) -> Gtk.Label: ...
|
|
| 29 |
+ @overload
|
|
| 30 |
+ def get(self, name: Literal['result_row_grid']) -> Gtk.Grid: ...
|
|
| 31 |
+ @overload
|
|
| 32 |
+ def get(self, name: Literal['row_name_label']) -> Gtk.Label: ...
|
|
| 33 |
+ @overload
|
|
| 34 |
+ def get(self, name: Literal['row_avatar']) -> Gtk.Image: ...
|
|
| 35 |
+ @overload
|
|
| 36 |
+ def get(self, name: Literal['row_time_label']) -> Gtk.Label: ...
|
|
| 37 |
+ @overload
|
|
| 38 |
+ def get(self, name: Literal['results_listbox']) -> Gtk.ListBox: ...
|
|
| 39 |
+ @overload
|
|
| 40 |
+ def get(self, name: Literal['search_box']) -> Gtk.Box: ...
|
|
| 41 |
+ def get(self, name: str) -> Any:
|
|
| 42 |
+ pass
|
| ... | ... | @@ -98,6 +98,7 @@ def __init__(self) -> None: |
| 98 | 98 |
self.set_wrap_mode(Gtk.WrapMode.WORD_CHAR)
|
| 99 | 99 |
self.set_top_margin(2)
|
| 100 | 100 |
self.set_bottom_margin(2)
|
| 101 |
+ self.set_monospace(True)
|
|
| 101 | 102 |
|
| 102 | 103 |
self._source_manager = GtkSource.LanguageManager.get_default()
|
| 103 | 104 |
self._style_scheme_manager = GtkSource.StyleSchemeManager.get_default()
|
| ... | ... | @@ -12,16 +12,16 @@ |
| 12 | 12 |
# You should have received a copy of the GNU General Public License
|
| 13 | 13 |
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
| 14 | 14 |
|
| 15 |
-from typing import Dict
|
|
| 16 |
-from typing import List
|
|
| 15 |
+from __future__ import annotations
|
|
| 16 |
+ |
|
| 17 |
+from typing import Any
|
|
| 18 |
+from typing import Union
|
|
| 17 | 19 |
from typing import Optional
|
| 18 |
-from typing import Tuple
|
|
| 19 | 20 |
|
| 20 | 21 |
import datetime
|
| 21 | 22 |
import logging
|
| 22 | 23 |
import time
|
| 23 | 24 |
import re
|
| 24 |
-from collections import namedtuple
|
|
| 25 | 25 |
|
| 26 | 26 |
from gi.repository import GObject
|
| 27 | 27 |
from gi.repository import Gtk
|
| ... | ... | @@ -37,7 +37,7 @@ |
| 37 | 37 |
from gajim.common.i18n import _
|
| 38 | 38 |
|
| 39 | 39 |
from .conversation.message_widget import MessageWidget
|
| 40 |
-from .util import get_builder
|
|
| 40 |
+from .builder import SearchViewBuilder
|
|
| 41 | 41 |
|
| 42 | 42 |
log = logging.getLogger('gajim.gui.search_view')
|
| 43 | 43 |
|
| ... | ... | @@ -56,12 +56,12 @@ def __init__(self) -> None: |
| 56 | 56 |
|
| 57 | 57 |
self._account: Optional[str] = None
|
| 58 | 58 |
self._jid: Optional[JID] = None
|
| 59 |
- self._results: List[str] = []
|
|
| 59 |
+ self._results: list[str] = []
|
|
| 60 | 60 |
self._scope: Optional[str] = None
|
| 61 | 61 |
|
| 62 |
- self._ui = get_builder('search_view.ui')
|
|
| 63 |
- self._ui.results_listbox.set_header_func(self._header_func)
|
|
| 64 |
- self.add(self._ui.search_box)
|
|
| 62 |
+ self._ui = SearchViewBuilder()
|
|
| 63 |
+ self._ui.get('results_listbox').set_header_func(self._header_func)
|
|
| 64 |
+ self.add(self._ui.get('search_box'))
|
|
| 65 | 65 |
|
| 66 | 66 |
self._ui.connect_signals(self)
|
| 67 | 67 |
|
| ... | ... | @@ -73,13 +73,15 @@ def __init__(self) -> None: |
| 73 | 73 |
self._on_account_state)
|
| 74 | 74 |
self.show_all()
|
| 75 | 75 |
|
| 76 |
- def _on_account_state(self, _event):
|
|
| 76 |
+ def _on_account_state(self, _event: Any) -> None:
|
|
| 77 | 77 |
self.clear()
|
| 78 | 78 |
|
| 79 | 79 |
@staticmethod
|
| 80 |
- def _header_func(row, before):
|
|
| 80 |
+ def _header_func(row: Union[CounterRow, ResultRow],
|
|
| 81 |
+ before: Optional[Union[CounterRow, ResultRow]]) -> None:
|
|
| 82 |
+ |
|
| 81 | 83 |
if before is None:
|
| 82 |
- if row.type == 'counter':
|
|
| 84 |
+ if isinstance(row, CounterRow):
|
|
| 83 | 85 |
row.set_header(None)
|
| 84 | 86 |
else:
|
| 85 | 87 |
row.set_header(RowHeader(row.account, row.jid, row.time))
|
| ... | ... | @@ -97,17 +99,17 @@ def _on_hide_clicked(self, _button: Gtk.Button) -> None: |
| 97 | 99 |
self.emit('hide-search')
|
| 98 | 100 |
|
| 99 | 101 |
def clear(self) -> None:
|
| 100 |
- self._ui.search_entry.set_text('')
|
|
| 102 |
+ self._ui.get('search_entry').set_text('')
|
|
| 101 | 103 |
self._clear_results()
|
| 102 | 104 |
|
| 103 | 105 |
def _clear_results(self) -> None:
|
| 104 |
- for row in self._ui.results_listbox.get_children():
|
|
| 105 |
- self._ui.results_listbox.remove(row)
|
|
| 106 |
+ for row in self._ui.get('results_listbox').get_children():
|
|
| 107 |
+ self._ui.get('results_listbox').remove(row)
|
|
| 106 | 108 |
row.destroy()
|
| 107 | 109 |
|
| 108 | 110 |
def _on_search(self, entry: Gtk.Entry) -> None:
|
| 109 | 111 |
self._clear_results()
|
| 110 |
- self._ui.date_hint.hide()
|
|
| 112 |
+ self._ui.get('date_hint').hide()
|
|
| 111 | 113 |
text = entry.get_text()
|
| 112 | 114 |
if not text:
|
| 113 | 115 |
return
|
| ... | ... | @@ -124,7 +126,7 @@ def _on_search(self, entry: Gtk.Entry) -> None: |
| 124 | 126 |
before_filters = min([datetime.datetime.fromisoformat(date) for
|
| 125 | 127 |
date in before_filters])
|
| 126 | 128 |
except ValueError:
|
| 127 |
- self._ui.date_hint.show()
|
|
| 129 |
+ self._ui.get('date_hint').show()
|
|
| 128 | 130 |
return
|
| 129 | 131 |
|
| 130 | 132 |
# after:date
|
| ... | ... | @@ -140,13 +142,13 @@ def _on_search(self, entry: Gtk.Entry) -> None: |
| 140 | 142 |
if after_filters.hour == after_filters.minute == 0:
|
| 141 | 143 |
after_filters += datetime.timedelta(days=1)
|
| 142 | 144 |
except ValueError:
|
| 143 |
- self._ui.date_hint.show()
|
|
| 145 |
+ self._ui.get('date_hint').show()
|
|
| 144 | 146 |
return
|
| 145 | 147 |
|
| 146 | 148 |
# has:'file'|'img'|'video'|filetype
|
| 147 | 149 |
text, has_filters = self._strip_filters(text, 'has')
|
| 148 | 150 |
|
| 149 |
- everywhere = self._ui.search_checkbutton.get_active()
|
|
| 151 |
+ everywhere = self._ui.get('search_checkbutton').get_active()
|
|
| 150 | 152 |
context = self._account is not None and self._jid is not None
|
| 151 | 153 |
|
| 152 | 154 |
if not context or everywhere:
|
| ... | ... | @@ -167,7 +169,7 @@ def _on_search(self, entry: Gtk.Entry) -> None: |
| 167 | 169 |
after=after_filters)
|
| 168 | 170 |
|
| 169 | 171 |
if has_filters is not None:
|
| 170 |
- filetypes = []
|
|
| 172 |
+ filetypes: list[str] = []
|
|
| 171 | 173 |
for filetype in has_filters:
|
| 172 | 174 |
types = FILE_CATEGORIES.get(filetype)
|
| 173 | 175 |
if types is None:
|
| ... | ... | @@ -180,9 +182,7 @@ def _on_search(self, entry: Gtk.Entry) -> None: |
| 180 | 182 |
self._add_counter()
|
| 181 | 183 |
self._add_results()
|
| 182 | 184 |
|
| 183 |
- def _filter_results_for_files(self,
|
|
| 184 |
- filetypes: List[str]
|
|
| 185 |
- ) -> None:
|
|
| 185 |
+ def _filter_results_for_files(self, filetypes: list[str]) -> None:
|
|
| 186 | 186 |
if 'file' in filetypes:
|
| 187 | 187 |
results = []
|
| 188 | 188 |
for result in self._results:
|
| ... | ... | @@ -202,9 +202,8 @@ def _filter_results_for_files(self, |
| 202 | 202 |
|
| 203 | 203 |
@staticmethod
|
| 204 | 204 |
def _strip_filters(text: str,
|
| 205 |
- filter_name: str
|
|
| 206 |
- ) -> Tuple[str, Optional[List[str]]]:
|
|
| 207 |
- filters = []
|
|
| 205 |
+ filter_name: str) -> tuple[str, Optional[list[str]]]:
|
|
| 206 |
+ filters: list[str] = []
|
|
| 208 | 207 |
start = 0
|
| 209 | 208 |
new_text = ''
|
| 210 | 209 |
for search_filter in re.finditer(filter_name + r':(\S+)\s?', text):
|
| ... | ... | @@ -218,7 +217,7 @@ def _strip_filters(text: str, |
| 218 | 217 |
def _add_counter(self) -> None:
|
| 219 | 218 |
results_count = len(self._results)
|
| 220 | 219 |
if results_count:
|
| 221 |
- self._ui.results_listbox.add(CounterRow(results_count))
|
|
| 220 |
+ self._ui.get('results_listbox').add(CounterRow(results_count))
|
|
| 222 | 221 |
|
| 223 | 222 |
def _add_results(self) -> None:
|
| 224 | 223 |
accounts = self._get_accounts()
|
| ... | ... | @@ -231,31 +230,27 @@ def _add_results(self) -> None: |
| 231 | 230 |
else:
|
| 232 | 231 |
result_row = ResultRow(msg, self._account, self._jid)
|
| 233 | 232 |
|
| 234 |
- self._ui.results_listbox.add(result_row)
|
|
| 233 |
+ self._ui.get('results_listbox').add(result_row)
|
|
| 235 | 234 |
self._results = self._results[25:]
|
| 236 | 235 |
|
| 237 | 236 |
def _on_edge_reached(self,
|
| 238 | 237 |
_scrolledwin: Gtk.ScrolledWindow,
|
| 239 |
- pos: Gtk.PositionType
|
|
| 240 |
- ) -> None:
|
|
| 238 |
+ pos: Gtk.PositionType) -> None:
|
|
| 241 | 239 |
if pos != Gtk.PositionType.BOTTOM:
|
| 242 | 240 |
return
|
| 243 | 241 |
|
| 244 | 242 |
self._add_results()
|
| 245 | 243 |
|
| 246 | 244 |
@staticmethod
|
| 247 |
- def _get_accounts() -> Dict[str, str]:
|
|
| 248 |
- accounts = {}
|
|
| 245 |
+ def _get_accounts() -> dict[str, str]:
|
|
| 246 |
+ accounts: dict[str, str] = {}
|
|
| 249 | 247 |
for account in app.settings.get_accounts():
|
| 250 | 248 |
account_id = app.storage.archive.get_account_id(account)
|
| 251 | 249 |
accounts[account_id] = account
|
| 252 | 250 |
return accounts
|
| 253 | 251 |
|
| 254 | 252 |
@staticmethod
|
| 255 |
- def _on_row_activated(_listbox: Gtk.ListBox, row: Gtk.ListBoxRow) -> None:
|
|
| 256 |
- if row.type == 'counter':
|
|
| 257 |
- return
|
|
| 258 |
- |
|
| 253 |
+ def _on_row_activated(_listbox: SearchView, row: ResultRow) -> None:
|
|
| 259 | 254 |
control = app.window.get_active_control()
|
| 260 | 255 |
if control is not None:
|
| 261 | 256 |
if control.contact.jid == row.jid:
|
| ... | ... | @@ -271,12 +266,12 @@ def _on_row_activated(_listbox: Gtk.ListBox, row: Gtk.ListBoxRow) -> None: |
| 271 | 266 |
control.scroll_to_message(row.log_line_id, row.timestamp)
|
| 272 | 267 |
|
| 273 | 268 |
def set_focus(self) -> None:
|
| 274 |
- self._ui.search_entry.grab_focus()
|
|
| 269 |
+ self._ui.get('search_entry').grab_focus()
|
|
| 275 | 270 |
|
| 276 | 271 |
def set_context(self, account: str, jid: JID) -> None:
|
| 277 | 272 |
self._account = account
|
| 278 | 273 |
self._jid = jid
|
| 279 |
- self._ui.search_checkbutton.set_active(jid is None)
|
|
| 274 |
+ self._ui.get('search_checkbutton').set_active(jid is None)
|
|
| 280 | 275 |
|
| 281 | 276 |
|
| 282 | 277 |
class RowHeader(Gtk.Box):
|
| ... | ... | @@ -284,16 +279,16 @@ def __init__(self, account: str, jid: JID, timestamp: float) -> None: |
| 284 | 279 |
Gtk.Box.__init__(self)
|
| 285 | 280 |
self.set_hexpand(True)
|
| 286 | 281 |
|
| 287 |
- self._ui = get_builder('search_view.ui')
|
|
| 288 |
- self.add(self._ui.header_box)
|
|
| 282 |
+ self._ui = SearchViewBuilder()
|
|
| 283 |
+ self.add(self._ui.get('header_box'))
|
|
| 289 | 284 |
|
| 290 | 285 |
client = app.get_client(account)
|
| 291 | 286 |
contact = client.get_module('Contacts').get_contact(jid)
|
| 292 |
- self._ui.header_name_label.set_text(contact.name or '')
|
|
| 287 |
+ self._ui.get('header_name_label').set_text(contact.name or '')
|
|
| 293 | 288 |
|
| 294 | 289 |
local_time = time.localtime(timestamp)
|
| 295 | 290 |
date = time.strftime('%x', local_time)
|
| 296 |
- self._ui.header_date_label.set_text(date)
|
|
| 291 |
+ self._ui.get('header_date_label').set_text(date)
|
|
| 297 | 292 |
|
| 298 | 293 |
self.show_all()
|
| 299 | 294 |
|
| ... | ... | @@ -302,9 +297,9 @@ class CounterRow(Gtk.ListBoxRow): |
| 302 | 297 |
def __init__(self, count: int) -> None:
|
| 303 | 298 |
Gtk.ListBoxRow.__init__(self)
|
| 304 | 299 |
self.set_activatable(False)
|
| 305 |
- self.type: str = 'counter'
|
|
| 306 |
- self.jid: str = '' # Has to be there for header_func
|
|
| 307 |
- self.time: float = 0.0
|
|
| 300 |
+ self.type = 'counter'
|
|
| 301 |
+ self.jid = '' # Has to be there for header_func
|
|
| 302 |
+ self.time = 0.0
|
|
| 308 | 303 |
self.get_style_context().add_class('search-view-counter')
|
| 309 | 304 |
|
| 310 | 305 |
if count == 1:
|
| ... | ... | @@ -317,7 +312,7 @@ def __init__(self, count: int) -> None: |
| 317 | 312 |
|
| 318 | 313 |
|
| 319 | 314 |
class ResultRow(Gtk.ListBoxRow):
|
| 320 |
- def __init__(self, msg: namedtuple, account: str, jid: JID) -> None:
|
|
| 315 |
+ def __init__(self, msg: Any, account: str, jid: JID) -> None:
|
|
| 321 | 316 |
Gtk.ListBoxRow.__init__(self)
|
| 322 | 317 |
self.account = account
|
| 323 | 318 |
self.jid = jid
|
| ... | ... | @@ -335,8 +330,8 @@ def __init__(self, msg: namedtuple, account: str, jid: JID) -> None: |
| 335 | 330 |
self.contact = self._client.get_module('Contacts').get_contact(
|
| 336 | 331 |
jid, groupchat=self.type == 'groupchat')
|
| 337 | 332 |
|
| 338 |
- self._ui = get_builder('search_view.ui')
|
|
| 339 |
- self.add(self._ui.result_row_grid)
|
|
| 333 |
+ self._ui = SearchViewBuilder()
|
|
| 334 |
+ self.add(self._ui.get('result_row_grid'))
|
|
| 340 | 335 |
|
| 341 | 336 |
kind = 'status'
|
| 342 | 337 |
contact_name = msg.contact_name
|
| ... | ... | @@ -350,25 +345,25 @@ def __init__(self, msg: namedtuple, account: str, jid: JID) -> None: |
| 350 | 345 |
KindConstant.SINGLE_MSG_SENT, KindConstant.CHAT_MSG_SENT):
|
| 351 | 346 |
kind = 'outgoing'
|
| 352 | 347 |
contact_name = app.nicks[account]
|
| 353 |
- self._ui.row_name_label.set_text(contact_name)
|
|
| 348 |
+ self._ui.get('row_name_label').set_text(contact_name)
|
|
| 354 | 349 |
|
| 355 | 350 |
avatar = self._get_avatar(kind, contact_name)
|
| 356 |
- self._ui.row_avatar.set_from_surface(avatar)
|
|
| 351 |
+ self._ui.get('row_avatar').set_from_surface(avatar)
|
|
| 357 | 352 |
|
| 358 | 353 |
local_time = time.localtime(msg.time)
|
| 359 | 354 |
date = time.strftime('%H:%M', local_time)
|
| 360 |
- self._ui.row_time_label.set_label(date)
|
|
| 355 |
+ self._ui.get('row_time_label').set_label(date)
|
|
| 361 | 356 |
|
| 362 | 357 |
message_widget = MessageWidget(account, selectable=False)
|
| 363 | 358 |
message_widget.add_with_styling(msg.message, nickname=contact_name)
|
| 364 |
- self._ui.result_row_grid.attach(message_widget, 1, 1, 2, 1)
|
|
| 359 |
+ self._ui.get('result_row_grid').attach(message_widget, 1, 1, 2, 1)
|
|
| 365 | 360 |
|
| 366 | 361 |
self.show_all()
|
| 367 | 362 |
|
| 368 | 363 |
def _get_avatar(self,
|
| 369 | 364 |
kind: str,
|
| 370 |
- name: str
|
|
| 371 |
- ) -> Optional[cairo.ImageSurface]:
|
|
| 365 |
+ name: str) -> Optional[cairo.ImageSurface]:
|
|
| 366 |
+ |
|
| 372 | 367 |
scale = self.get_scale_factor()
|
| 373 | 368 |
if self.contact.is_groupchat:
|
| 374 | 369 |
contact = self.contact.get_resource(name)
|
| ... | ... | @@ -21,14 +21,11 @@ |
| 21 | 21 |
from typing import Optional
|
| 22 | 22 |
from typing import Union
|
| 23 | 23 |
|
| 24 |
-import sys
|
|
| 25 | 24 |
import logging
|
| 26 | 25 |
import math
|
| 27 | 26 |
import textwrap
|
| 28 |
-import functools
|
|
| 29 | 27 |
from io import BytesIO
|
| 30 | 28 |
from importlib import import_module
|
| 31 |
-import xml.etree.ElementTree as ET
|
|
| 32 | 29 |
from functools import wraps
|
| 33 | 30 |
from functools import lru_cache
|
| 34 | 31 |
|
| ... | ... | @@ -40,7 +37,6 @@ |
| 40 | 37 |
from gi.repository import Gdk
|
| 41 | 38 |
from gi.repository import Gtk
|
| 42 | 39 |
from gi.repository import GLib
|
| 43 |
-from gi.repository import Gio
|
|
| 44 | 40 |
from gi.repository import Pango
|
| 45 | 41 |
from gi.repository import GdkPixbuf
|
| 46 | 42 |
import nbxmpp
|
| ... | ... | @@ -48,7 +44,6 @@ |
| 48 | 44 |
|
| 49 | 45 |
from gajim.common import app
|
| 50 | 46 |
from gajim.common import configpaths
|
| 51 |
-from gajim.common import i18n
|
|
| 52 | 47 |
from gajim.common.i18n import _
|
| 53 | 48 |
from gajim.common.helpers import URL_REGEX
|
| 54 | 49 |
from gajim.common.const import LOCATION_DATA
|
| ... | ... | @@ -59,11 +54,13 @@ |
| 59 | 54 |
|
| 60 | 55 |
from .const import GajimIconSet
|
| 61 | 56 |
from .const import WINDOW_MODULES
|
| 57 |
+from .builder import Builder
|
|
| 62 | 58 |
|
| 63 | 59 |
_icon_theme = Gtk.IconTheme.get_default()
|
| 64 | 60 |
if _icon_theme is not None:
|
| 65 | 61 |
_icon_theme.append_search_path(str(configpaths.get('ICONS')))
|
| 66 | 62 |
|
| 63 |
+ |
|
| 67 | 64 |
log = logging.getLogger('gajim.gui.util')
|
| 68 | 65 |
|
| 69 | 66 |
|
| ... | ... | @@ -150,55 +147,6 @@ def nick_matching(nick: str) -> bool: |
| 150 | 147 |
return matches + other_nicks
|
| 151 | 148 |
|
| 152 | 149 |
|
| 153 |
-class Builder:
|
|
| 154 |
- def __init__(self,
|
|
| 155 |
- filename: str,
|
|
| 156 |
- widgets: List[str] = None,
|
|
| 157 |
- domain: str = None,
|
|
| 158 |
- gettext_: Any = None) -> None:
|
|
| 159 |
- self._builder = Gtk.Builder()
|
|
| 160 |
- |
|
| 161 |
- if domain is None:
|
|
| 162 |
- domain = i18n.DOMAIN
|
|
| 163 |
- self._builder.set_translation_domain(domain)
|
|
| 164 |
- |
|
| 165 |
- if gettext_ is None:
|
|
| 166 |
- gettext_ = _
|
|
| 167 |
- |
|
| 168 |
- xml_text = self._load_string_from_filename(filename, gettext_)
|
|
| 169 |
- |
|
| 170 |
- if widgets is not None:
|
|
| 171 |
- self._builder.add_objects_from_string(xml_text, widgets)
|
|
| 172 |
- else:
|
|
| 173 |
- self._builder.add_from_string(xml_text)
|
|
| 174 |
- |
|
| 175 |
- @staticmethod
|
|
| 176 |
- @functools.lru_cache(maxsize=None)
|
|
| 177 |
- def _load_string_from_filename(filename, gettext_):
|
|
| 178 |
- file_path = str(configpaths.get('GUI') / filename)
|
|
| 179 |
- |
|
| 180 |
- if sys.platform == "win32":
|
|
| 181 |
- # This is a workaround for non working translation on Windows
|
|
| 182 |
- tree = ET.parse(file_path)
|
|
| 183 |
- for node in tree.iter():
|
|
| 184 |
- if 'translatable' in node.attrib and node.text is not None:
|
|
| 185 |
- node.text = gettext_(node.text)
|
|
| 186 |
- |
|
| 187 |
- return ET.tostring(tree.getroot(),
|
|
| 188 |
- encoding='unicode',
|
|
| 189 |
- method='xml')
|
|
| 190 |
- |
|
| 191 |
- file = Gio.File.new_for_path(file_path)
|
|
| 192 |
- content = file.load_contents(None)
|
|
| 193 |
- return content[1].decode()
|
|
| 194 |
- |
|
| 195 |
- def __getattr__(self, name):
|
|
| 196 |
- try:
|
|
| 197 |
- return getattr(self._builder, name)
|
|
| 198 |
- except AttributeError:
|
|
| 199 |
- return self._builder.get_object(name)
|
|
| 200 |
- |
|
| 201 |
- |
|
| 202 | 150 |
def get_builder(file_name: str, widgets: List[str] = None) -> Builder:
|
| 203 | 151 |
return Builder(file_name, widgets)
|
| 204 | 152 |
|