Fix issues with RTL languages and LAT/LONG (#922)
* Fix display of GPS coordinates in Places view for RTL languages Issue #11335 * Fix place editor lat/long entry for RTL languages Fixes #11335
This commit is contained in:
parent
2054c467db
commit
1025a38caa
@ -158,24 +158,30 @@ class EditPlace(EditPrimary):
|
|||||||
self.obj.set_code, self.obj.get_code,
|
self.obj.set_code, self.obj.get_code,
|
||||||
self.db.readonly)
|
self.db.readonly)
|
||||||
|
|
||||||
|
entry = self.top.get_object("lon_entry")
|
||||||
|
entry.set_ltr_mode()
|
||||||
self.longitude = MonitoredEntry(
|
self.longitude = MonitoredEntry(
|
||||||
self.top.get_object("lon_entry"),
|
entry,
|
||||||
self.obj.set_longitude, self.obj.get_longitude,
|
self.obj.set_longitude, self.obj.get_longitude,
|
||||||
self.db.readonly)
|
self.db.readonly)
|
||||||
self.longitude.connect("validate", self._validate_coordinate, "lon")
|
self.longitude.connect("validate", self._validate_coordinate, "lon")
|
||||||
#force validation now with initial entry
|
#force validation now with initial entry
|
||||||
self.top.get_object("lon_entry").validate(force=True)
|
entry.validate(force=True)
|
||||||
|
|
||||||
|
entry = self.top.get_object("lat_entry")
|
||||||
|
entry.set_ltr_mode()
|
||||||
self.latitude = MonitoredEntry(
|
self.latitude = MonitoredEntry(
|
||||||
self.top.get_object("lat_entry"),
|
entry,
|
||||||
self.obj.set_latitude, self.obj.get_latitude,
|
self.obj.set_latitude, self.obj.get_latitude,
|
||||||
self.db.readonly)
|
self.db.readonly)
|
||||||
self.latitude.connect("validate", self._validate_coordinate, "lat")
|
self.latitude.connect("validate", self._validate_coordinate, "lat")
|
||||||
#force validation now with initial entry
|
#force validation now with initial entry
|
||||||
self.top.get_object("lat_entry").validate(force=True)
|
entry.validate(force=True)
|
||||||
|
|
||||||
|
entry = self.top.get_object("latlon_entry")
|
||||||
|
entry.set_ltr_mode()
|
||||||
self.latlon = MonitoredEntry(
|
self.latlon = MonitoredEntry(
|
||||||
self.top.get_object("latlon_entry"),
|
entry,
|
||||||
self.set_latlongitude, self.get_latlongitude,
|
self.set_latlongitude, self.get_latlongitude,
|
||||||
self.db.readonly)
|
self.db.readonly)
|
||||||
|
|
||||||
|
@ -151,24 +151,30 @@ class EditPlaceRef(EditReference):
|
|||||||
self.source.set_code, self.source.get_code,
|
self.source.set_code, self.source.get_code,
|
||||||
self.db.readonly)
|
self.db.readonly)
|
||||||
|
|
||||||
|
entry = self.top.get_object("lon_entry")
|
||||||
|
entry.set_ltr_mode()
|
||||||
self.longitude = MonitoredEntry(
|
self.longitude = MonitoredEntry(
|
||||||
self.top.get_object("lon_entry"),
|
entry,
|
||||||
self.source.set_longitude, self.source.get_longitude,
|
self.source.set_longitude, self.source.get_longitude,
|
||||||
self.db.readonly)
|
self.db.readonly)
|
||||||
self.longitude.connect("validate", self._validate_coordinate, "lon")
|
self.longitude.connect("validate", self._validate_coordinate, "lon")
|
||||||
#force validation now with initial entry
|
#force validation now with initial entry
|
||||||
self.top.get_object("lon_entry").validate(force=True)
|
entry.validate(force=True)
|
||||||
|
|
||||||
|
entry = self.top.get_object("lat_entry")
|
||||||
|
entry.set_ltr_mode()
|
||||||
self.latitude = MonitoredEntry(
|
self.latitude = MonitoredEntry(
|
||||||
self.top.get_object("lat_entry"),
|
entry,
|
||||||
self.source.set_latitude, self.source.get_latitude,
|
self.source.set_latitude, self.source.get_latitude,
|
||||||
self.db.readonly)
|
self.db.readonly)
|
||||||
self.latitude.connect("validate", self._validate_coordinate, "lat")
|
self.latitude.connect("validate", self._validate_coordinate, "lat")
|
||||||
#force validation now with initial entry
|
#force validation now with initial entry
|
||||||
self.top.get_object("lat_entry").validate(force=True)
|
entry.validate(force=True)
|
||||||
|
|
||||||
|
entry = self.top.get_object("latlon_entry")
|
||||||
|
entry.set_ltr_mode()
|
||||||
self.latlon = MonitoredEntry(
|
self.latlon = MonitoredEntry(
|
||||||
self.top.get_object("latlon_entry"),
|
entry,
|
||||||
self.set_latlongitude, self.get_latlongitude,
|
self.set_latlongitude, self.get_latlongitude,
|
||||||
self.db.readonly)
|
self.db.readonly)
|
||||||
|
|
||||||
|
@ -143,7 +143,7 @@ class PlaceBaseModel:
|
|||||||
value = conv_lat_lon('0', data[3], format='DEG')[1]
|
value = conv_lat_lon('0', data[3], format='DEG')[1]
|
||||||
if not value:
|
if not value:
|
||||||
return _("Error in format")
|
return _("Error in format")
|
||||||
return value
|
return ("\u202d" + value + "\u202e") if glocale.rtl_locale else value
|
||||||
|
|
||||||
def column_latitude(self, data):
|
def column_latitude(self, data):
|
||||||
if not data[4]:
|
if not data[4]:
|
||||||
@ -151,7 +151,7 @@ class PlaceBaseModel:
|
|||||||
value = conv_lat_lon(data[4], '0', format='DEG')[0]
|
value = conv_lat_lon(data[4], '0', format='DEG')[0]
|
||||||
if not value:
|
if not value:
|
||||||
return _("Error in format")
|
return _("Error in format")
|
||||||
return value
|
return ("\u202d" + value + "\u202e") if glocale.rtl_locale else value
|
||||||
|
|
||||||
def sort_longitude(self, data):
|
def sort_longitude(self, data):
|
||||||
if not data[3]:
|
if not data[3]:
|
||||||
|
@ -46,10 +46,10 @@ from gi.repository import Gtk
|
|||||||
#
|
#
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
from .undoablebuffer import Stack
|
from .undoablebuffer import Stack
|
||||||
|
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||||
|
|
||||||
# table for skipping illegal control chars
|
# table for skipping illegal control chars
|
||||||
INVISIBLE = dict.fromkeys(list(range(32)))
|
INVISIBLE = dict.fromkeys(list(range(32)) + [0x202d, 0x202e])
|
||||||
|
|
||||||
|
|
||||||
class UndoableInsertEntry:
|
class UndoableInsertEntry:
|
||||||
@ -89,6 +89,10 @@ class UndoableEntry(Gtk.Entry, Gtk.Editable):
|
|||||||
|
|
||||||
Additional features:
|
Additional features:
|
||||||
- Undo and Redo on CTRL-Z/CTRL-SHIFT-Z
|
- Undo and Redo on CTRL-Z/CTRL-SHIFT-Z
|
||||||
|
|
||||||
|
- ltr_mode (forces the field to always be left to right, useful for GPS
|
||||||
|
coordinates and similar numbers that might contain RTL characters.
|
||||||
|
See set_ltr_mode.
|
||||||
"""
|
"""
|
||||||
__gtype_name__ = 'UndoableEntry'
|
__gtype_name__ = 'UndoableEntry'
|
||||||
|
|
||||||
@ -99,12 +103,12 @@ class UndoableEntry(Gtk.Entry, Gtk.Editable):
|
|||||||
undo_stack_size = 50
|
undo_stack_size = 50
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
Gtk.Entry.__init__(self)
|
|
||||||
self.undo_stack = Stack(self.undo_stack_size)
|
self.undo_stack = Stack(self.undo_stack_size)
|
||||||
self.redo_stack = []
|
self.redo_stack = []
|
||||||
self.not_undoable_action = False
|
self.not_undoable_action = False
|
||||||
self.undo_in_progress = False
|
self.undo_in_progress = False
|
||||||
|
self.ltr_mode = False
|
||||||
|
Gtk.Entry.__init__(self)
|
||||||
self.connect('delete-text', self._on_delete_text)
|
self.connect('delete-text', self._on_delete_text)
|
||||||
self.connect('key-press-event', self._on_key_press_event)
|
self.connect('key-press-event', self._on_key_press_event)
|
||||||
|
|
||||||
@ -162,10 +166,16 @@ class UndoableEntry(Gtk.Entry, Gtk.Editable):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
text = text.translate(INVISIBLE)
|
text = text.translate(INVISIBLE)
|
||||||
|
if self.ltr_mode:
|
||||||
|
if position == 0:
|
||||||
|
position = 1
|
||||||
|
elif position >= self.get_text_length():
|
||||||
|
position -= 1
|
||||||
|
|
||||||
if not self.undo_in_progress:
|
if not self.undo_in_progress:
|
||||||
self.__empty_redo_stack()
|
self.__empty_redo_stack()
|
||||||
while not self.not_undoable_action:
|
while not self.not_undoable_action:
|
||||||
undo_action = self.insertclass(text, length, self.get_position())
|
undo_action = self.insertclass(text, length, position)
|
||||||
try:
|
try:
|
||||||
prev_insert = self.undo_stack.pop()
|
prev_insert = self.undo_stack.pop()
|
||||||
except IndexError:
|
except IndexError:
|
||||||
@ -186,6 +196,7 @@ class UndoableEntry(Gtk.Entry, Gtk.Editable):
|
|||||||
self.get_buffer().insert_text(position, text, len(text))
|
self.get_buffer().insert_text(position, text, len(text))
|
||||||
return position + len(text)
|
return position + len(text)
|
||||||
|
|
||||||
|
|
||||||
def _on_delete_text(self, editable, start, end):
|
def _on_delete_text(self, editable, start, end):
|
||||||
def can_be_merged(prev, cur):
|
def can_be_merged(prev, cur):
|
||||||
"""
|
"""
|
||||||
@ -212,20 +223,33 @@ class UndoableEntry(Gtk.Entry, Gtk.Editable):
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
if self.ltr_mode: # limit deletes to area between LRO/PDF
|
||||||
|
if start == 0:
|
||||||
|
start = 1
|
||||||
|
elif start > self.get_text_length() - 1:
|
||||||
|
start -= 1
|
||||||
|
if end == 0:
|
||||||
|
end = 1
|
||||||
|
elif end > self.get_text_length() - 1:
|
||||||
|
end -= 1
|
||||||
|
elif end < 0:
|
||||||
|
end = self.get_text_length() - 1
|
||||||
|
|
||||||
|
while True:
|
||||||
if not self.undo_in_progress:
|
if not self.undo_in_progress:
|
||||||
self.__empty_redo_stack()
|
self.__empty_redo_stack()
|
||||||
if self.not_undoable_action:
|
if self.not_undoable_action:
|
||||||
return
|
break
|
||||||
undo_action = self.deleteclass(editable, start, end)
|
undo_action = self.deleteclass(self, start, end)
|
||||||
try:
|
try:
|
||||||
prev_delete = self.undo_stack.pop()
|
prev_delete = self.undo_stack.pop()
|
||||||
except IndexError:
|
except IndexError:
|
||||||
self.undo_stack.append(undo_action)
|
self.undo_stack.append(undo_action)
|
||||||
return
|
break
|
||||||
if not isinstance(prev_delete, self.deleteclass):
|
if not isinstance(prev_delete, self.deleteclass):
|
||||||
self.undo_stack.append(prev_delete)
|
self.undo_stack.append(prev_delete)
|
||||||
self.undo_stack.append(undo_action)
|
self.undo_stack.append(undo_action)
|
||||||
return
|
break
|
||||||
if can_be_merged(prev_delete, undo_action):
|
if can_be_merged(prev_delete, undo_action):
|
||||||
if prev_delete.start == undo_action.start: # delete key used
|
if prev_delete.start == undo_action.start: # delete key used
|
||||||
prev_delete.text += undo_action.text
|
prev_delete.text += undo_action.text
|
||||||
@ -238,6 +262,10 @@ class UndoableEntry(Gtk.Entry, Gtk.Editable):
|
|||||||
else:
|
else:
|
||||||
self.undo_stack.append(prev_delete)
|
self.undo_stack.append(prev_delete)
|
||||||
self.undo_stack.append(undo_action)
|
self.undo_stack.append(undo_action)
|
||||||
|
break
|
||||||
|
self.get_buffer().delete_text(start, end - start)
|
||||||
|
self.stop_emission_by_name('delete-text')
|
||||||
|
return True
|
||||||
|
|
||||||
def begin_not_undoable_action(self):
|
def begin_not_undoable_action(self):
|
||||||
"""don't record the next actions
|
"""don't record the next actions
|
||||||
@ -329,3 +357,40 @@ class UndoableEntry(Gtk.Entry, Gtk.Editable):
|
|||||||
|
|
||||||
def _handle_redo(self, redo_action):
|
def _handle_redo(self, redo_action):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def set_ltr_mode(self):
|
||||||
|
""" sets up the Entry to always be in LTR left to right even if some
|
||||||
|
characters are RTL.
|
||||||
|
This works by inserting the LRO/PDF Unicode Explicit Directional
|
||||||
|
Override characters around the entry text. These characters are then
|
||||||
|
protected agains insert/delete operations.
|
||||||
|
|
||||||
|
This call must be made before other text is inserted to the Entry.
|
||||||
|
|
||||||
|
Note: we only enable this during rtl_local languages because it has a
|
||||||
|
minor consequence; if cutting a field from this Entry with this mode
|
||||||
|
enabled, the LRO/PDF characters may end up in the clipboard. If pasted
|
||||||
|
back into another UndoableEntry, this is ignored, but if pasted in
|
||||||
|
another app it may be noticable.
|
||||||
|
"""
|
||||||
|
if glocale.rtl_locale:
|
||||||
|
self.get_buffer().set_text("\u202d\u202e", -1)
|
||||||
|
self.ltr_mode = True
|
||||||
|
|
||||||
|
def do_set_position(self, position):
|
||||||
|
""" In ltr_mode, this ensures that the cursor cannot be put outside
|
||||||
|
the LRO/PDF characters on the ends of the buffer. """
|
||||||
|
if position < 0:
|
||||||
|
position = self.get_text_length()
|
||||||
|
if self.ltr_mode:
|
||||||
|
if position == 0:
|
||||||
|
position = 1
|
||||||
|
elif position == self.get_text_length():
|
||||||
|
position -= 1
|
||||||
|
Gtk.Editable.select_region(self, position, position)
|
||||||
|
|
||||||
|
def get_text(self):
|
||||||
|
""" Used to remove the LRO/PDF characters when in ltr_mode.
|
||||||
|
"""
|
||||||
|
text = Gtk.Entry.get_text(self)
|
||||||
|
return text[1:-1] if self.ltr_mode else text
|
||||||
|
Loading…
Reference in New Issue
Block a user