mirror of
https://github.com/DYefremov/DemonEditor.git
synced 2026-01-14 19:43:05 +01:00
reworked and improved dnd for lists
This commit is contained in:
@@ -109,12 +109,14 @@ class Application(Gtk.Application):
|
||||
"on_fav_view_query_tooltip": self.on_fav_view_query_tooltip,
|
||||
"on_services_view_query_tooltip": self.on_services_view_query_tooltip,
|
||||
"on_view_drag_begin": self.on_view_drag_begin,
|
||||
"on_view_drag_end": self.on_view_drag_end,
|
||||
"on_view_drag_data_get": self.on_view_drag_data_get,
|
||||
"on_services_view_drag_drop": self.on_services_view_drag_drop,
|
||||
"on_services_view_drag_data_received": self.on_services_view_drag_data_received,
|
||||
"on_view_drag_data_received": self.on_view_drag_data_received,
|
||||
"on_bq_view_drag_data_received": self.on_bq_view_drag_data_received,
|
||||
"on_view_press": self.on_view_press,
|
||||
"on_view_release": self.on_view_release,
|
||||
"on_view_popup_menu": self.on_view_popup_menu,
|
||||
"on_view_focus": self.on_view_focus,
|
||||
"on_model_changed": self.on_model_changed,
|
||||
@@ -179,6 +181,7 @@ class Application(Gtk.Application):
|
||||
self._blacklist = set()
|
||||
self._current_bq_name = None
|
||||
self._bq_selected = "" # Current selected bouquet
|
||||
self._select_enabled = True # Multiple selection
|
||||
# Current satellite positions in the services list
|
||||
self._sat_positions = []
|
||||
self._marker_types = {BqServiceType.MARKER.name, BqServiceType.SPACE.name}
|
||||
@@ -461,6 +464,10 @@ class Application(Gtk.Application):
|
||||
|
||||
self._app_info_box.drag_dest_set(Gtk.DestDefaults.ALL, [], Gdk.DragAction.COPY)
|
||||
self._app_info_box.drag_dest_add_text_targets()
|
||||
# For multiple selection.
|
||||
self._services_view.get_selection().set_select_function(lambda *args: self._select_enabled)
|
||||
self._fav_view.get_selection().set_select_function(lambda *args: self._select_enabled)
|
||||
self._bouquets_view.get_selection().set_select_function(lambda *args: self._select_enabled)
|
||||
|
||||
def init_colors(self, update=False):
|
||||
""" Initialisation of background colors for the services.
|
||||
@@ -722,6 +729,8 @@ class Application(Gtk.Application):
|
||||
self._bouquets.pop("{}:{}".format(b_row[Column.BQ_NAME], b_row[Column.BQ_TYPE]), None)
|
||||
self._bouquets_model.remove(itr)
|
||||
|
||||
self._bq_selected = ""
|
||||
self._bq_name_label.set_text(self._bq_selected)
|
||||
self._wait_dialog.hide()
|
||||
yield True
|
||||
|
||||
@@ -955,22 +964,83 @@ class Application(Gtk.Application):
|
||||
|
||||
def get_ssid_info(self, srv):
|
||||
""" Returns SID representation in hex and dec formats. """
|
||||
sid = srv.ssid or "0"
|
||||
try:
|
||||
dec = "{0:04d}".format(int(srv.ssid, 16))
|
||||
dec = "{0:04d}".format(int(sid, 16))
|
||||
except ValueError as e:
|
||||
log("SID value conversion error: {}".format(e))
|
||||
else:
|
||||
return "SID: 0x{} ({})".format(srv.ssid.upper(), dec)
|
||||
return "SID: 0x{} ({})".format(sid.upper(), dec)
|
||||
|
||||
return "SID: 0x{}".format(srv.ssid.upper())
|
||||
return "SID: 0x{}".format(sid.upper())
|
||||
|
||||
# ***************** Drag-and-drop *********************#
|
||||
|
||||
def on_view_drag_begin(self, view, context):
|
||||
""" Selects a row under the cursor in the view at the dragging beginning. """
|
||||
path, column = view.get_cursor()
|
||||
if path:
|
||||
view.get_selection().select_path(path)
|
||||
""" Sets its own icon for dragging.
|
||||
|
||||
We have to use "connect_after" (after="yes" in xml) to override what the default handler did.
|
||||
https://lazka.github.io/pgi-docs/Gtk-3.0/classes/Widget.html#Gtk.Widget.signals.drag_begin
|
||||
"""
|
||||
model, paths = view.get_selection().get_selected_rows()
|
||||
if len(paths) < 1:
|
||||
return
|
||||
|
||||
name, model = get_model_data(view)
|
||||
name_column, type_column = Column.SRV_SERVICE, Column.SRV_TYPE
|
||||
if name == self.FAV_MODEL_NAME:
|
||||
name_column, type_column = Column.FAV_SERVICE, Column.FAV_TYPE
|
||||
elif name == self.BQ_MODEL_NAME:
|
||||
name_column, type_column = Column.BQ_NAME, Column.BQ_TYPE
|
||||
# https://stackoverflow.com/a/52248549
|
||||
Gtk.drag_set_icon_widget(context, self.get_drag_widget(model, paths, name_column, type_column), 0, 0)
|
||||
|
||||
def on_view_drag_end(self, view: Gtk.TreeView, context):
|
||||
self._select_enabled = True
|
||||
view.get_selection().unselect_all()
|
||||
|
||||
def get_drag_widget(self, model, paths, text_column, type_column):
|
||||
""" Creates and returns a widget for a dragging icon. """
|
||||
frame = Gtk.Frame()
|
||||
frame.set_border_width(2)
|
||||
list_box = Gtk.ListBox()
|
||||
padding = 10
|
||||
for index, row in enumerate([model[p] for p in paths]):
|
||||
if index == 25:
|
||||
list_box.add(Gtk.Arrow(Gtk.ArrowType.DOWN))
|
||||
break
|
||||
|
||||
h_box = Gtk.HBox()
|
||||
h_box.set_spacing(10)
|
||||
s_context = h_box.get_style_context()
|
||||
s_context.add_class(Gtk.STYLE_CLASS_LIST_ROW)
|
||||
label = Gtk.Label(row[text_column])
|
||||
label.set_alignment(0, 0)
|
||||
label.set_padding(padding, 2)
|
||||
h_box.add(label)
|
||||
label = Gtk.Label(row[type_column])
|
||||
label.set_halign(Gtk.Align.END)
|
||||
label.set_padding(padding, 2)
|
||||
h_box.add(label)
|
||||
list_box.add(h_box)
|
||||
|
||||
if len(paths) > 1:
|
||||
list_box.add(Gtk.Separator())
|
||||
h_box = Gtk.HBox()
|
||||
h_box.set_spacing(2)
|
||||
img = Gtk.Image.new_from_icon_name("document-properties", 0)
|
||||
h_box.add(img)
|
||||
h_box.add(Gtk.Label(len(paths)))
|
||||
h_box.set_halign(Gtk.Align.START)
|
||||
h_box.set_margin_left(10)
|
||||
h_box.set_margin_bottom(5)
|
||||
h_box.set_margin_top(2)
|
||||
list_box.add(h_box)
|
||||
|
||||
frame.add(list_box)
|
||||
frame.show_all()
|
||||
|
||||
return frame
|
||||
|
||||
def on_view_drag_data_get(self, view, drag_context, data, info, time):
|
||||
selection = self.get_selection(view)
|
||||
@@ -993,10 +1063,13 @@ class Application(Gtk.Application):
|
||||
txt = data.get_text()
|
||||
uris = data.get_uris()
|
||||
if txt:
|
||||
if txt.startswith("file://"):
|
||||
name, model = get_model_data(view)
|
||||
if txt.startswith("file://") and name == self.SERVICE_MODEL_NAME:
|
||||
self.on_import_data(urlparse(unquote(txt)).path.strip())
|
||||
else:
|
||||
elif name == self.FAV_MODEL_NAME:
|
||||
self.receive_selection(view=view, drop_info=view.get_dest_row_at_pos(x, y), data=txt)
|
||||
else:
|
||||
self.show_error_dialog("Not allowed in this context!")
|
||||
elif len(uris) == 2:
|
||||
self.picons_buffer = self.on_assign_picon(view, urlparse(unquote(uris[0])).path,
|
||||
urlparse(unquote(uris[1])).path + os.sep)
|
||||
@@ -1029,7 +1102,8 @@ class Application(Gtk.Application):
|
||||
if not p_itr:
|
||||
break
|
||||
if p_itr and model.get_path(p_itr)[0] == p_path:
|
||||
top_iter = model.move_before(itr, top_iter)
|
||||
model.move_after(itr, top_iter)
|
||||
top_iter = itr
|
||||
else:
|
||||
model.insert(parent_itr, model.get_path(top_iter)[1], model[itr][:])
|
||||
to_del.append(itr)
|
||||
@@ -1099,10 +1173,22 @@ class Application(Gtk.Application):
|
||||
self.show_error_dialog(str(e))
|
||||
|
||||
def on_view_press(self, view, event):
|
||||
""" Handles a mouse click (press) to view. """
|
||||
if event.get_event_type() == Gdk.EventType.BUTTON_PRESS and event.button == Gdk.BUTTON_PRIMARY:
|
||||
target = view.get_path_at_pos(event.x, event.y)
|
||||
# Idea taken from here: https://kevinmehall.net/2010/pygtk_multi_select_drag_drop
|
||||
mask = not (event.state & (Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.SHIFT_MASK))
|
||||
if target and mask and view.get_selection().path_is_selected(target[0]):
|
||||
self._select_enabled = False
|
||||
|
||||
name, model = get_model_data(view)
|
||||
self.delete_views_selection(name)
|
||||
|
||||
def on_view_release(self, view, event):
|
||||
""" Handles a mouse click (release) to view. """
|
||||
# Enable selection.
|
||||
self._select_enabled = True
|
||||
|
||||
def delete_views_selection(self, name):
|
||||
if name == self.SERVICE_MODEL_NAME:
|
||||
self.delete_selection(self._fav_view)
|
||||
|
||||
@@ -46,54 +46,57 @@ def move_items(key, view: Gtk.TreeView):
|
||||
""" Move items in the tree view """
|
||||
selection = view.get_selection()
|
||||
model, paths = selection.get_selected_rows()
|
||||
if not paths:
|
||||
return
|
||||
|
||||
if paths:
|
||||
mod_length = len(model)
|
||||
if mod_length == len(paths):
|
||||
return
|
||||
cursor_path = view.get_cursor()[0]
|
||||
max_path = Gtk.TreePath.new_from_indices((mod_length,))
|
||||
min_path = Gtk.TreePath.new_from_indices((0,))
|
||||
is_tree_store = type(model) is Gtk.TreeStore
|
||||
mod_length = len(model)
|
||||
if not is_tree_store and mod_length == len(paths):
|
||||
return
|
||||
|
||||
cursor_path = view.get_cursor()[0]
|
||||
max_path = Gtk.TreePath.new_from_indices((mod_length,))
|
||||
min_path = Gtk.TreePath.new_from_indices((0,))
|
||||
|
||||
if is_tree_store:
|
||||
is_tree_store = False
|
||||
parent_paths = list(filter(lambda p: p.get_depth() == 1, paths))
|
||||
if parent_paths:
|
||||
paths = parent_paths
|
||||
min_path = model.get_path(model.get_iter_first())
|
||||
view.collapse_all()
|
||||
if mod_length == len(paths):
|
||||
return
|
||||
else:
|
||||
if not is_some_level(paths):
|
||||
return
|
||||
parent_itr = model.iter_parent(model.get_iter(paths[0]))
|
||||
parent_index = model.get_path(parent_itr)
|
||||
children_num = model.iter_n_children(parent_itr)
|
||||
if key in (KeyboardKey.PAGE_DOWN, KeyboardKey.END, KeyboardKey.END_KP, KeyboardKey.PAGE_DOWN_KP):
|
||||
children_num -= 1
|
||||
min_path = Gtk.TreePath.new_from_string("{}:{}".format(parent_index, 0))
|
||||
max_path = Gtk.TreePath.new_from_string("{}:{}".format(parent_index, children_num))
|
||||
is_tree_store = True
|
||||
|
||||
if type(model) is Gtk.TreeStore:
|
||||
parent_paths = list(filter(lambda p: p.get_depth() == 1, paths))
|
||||
if parent_paths:
|
||||
paths = parent_paths
|
||||
min_path = model.get_path(model.get_iter_first())
|
||||
view.collapse_all()
|
||||
if mod_length == len(paths):
|
||||
return
|
||||
else:
|
||||
if not is_some_level(paths):
|
||||
return
|
||||
parent_itr = model.iter_parent(model.get_iter(paths[0]))
|
||||
parent_index = model.get_path(parent_itr)
|
||||
children_num = model.iter_n_children(parent_itr)
|
||||
if key in (KeyboardKey.PAGE_DOWN, KeyboardKey.END, KeyboardKey.END_KP, KeyboardKey.PAGE_DOWN_KP):
|
||||
children_num -= 1
|
||||
min_path = Gtk.TreePath.new_from_string("{}:{}".format(parent_index, 0))
|
||||
max_path = Gtk.TreePath.new_from_string("{}:{}".format(parent_index, children_num))
|
||||
is_tree_store = True
|
||||
|
||||
if key is KeyboardKey.UP:
|
||||
top_path = Gtk.TreePath(paths[0])
|
||||
set_cursor(top_path, paths, selection, view)
|
||||
top_path.prev()
|
||||
move_up(top_path, model, paths)
|
||||
elif key is KeyboardKey.DOWN:
|
||||
down_path = Gtk.TreePath(paths[-1])
|
||||
set_cursor(down_path, paths, selection, view)
|
||||
down_path.next()
|
||||
if down_path < max_path:
|
||||
move_down(down_path, model, paths)
|
||||
else:
|
||||
max_path.prev()
|
||||
move_down(max_path, model, paths)
|
||||
elif key in (KeyboardKey.PAGE_UP, KeyboardKey.HOME, KeyboardKey.PAGE_UP_KP, KeyboardKey.HOME_KP):
|
||||
move_up(min_path if is_tree_store else cursor_path, model, paths)
|
||||
elif key in (KeyboardKey.PAGE_DOWN, KeyboardKey.END, KeyboardKey.END_KP, KeyboardKey.PAGE_DOWN_KP):
|
||||
move_down(max_path if is_tree_store else cursor_path, model, paths)
|
||||
if key is KeyboardKey.UP:
|
||||
top_path = Gtk.TreePath(paths[0])
|
||||
set_cursor(top_path, paths, selection, view)
|
||||
top_path.prev()
|
||||
move_up(top_path, model, paths)
|
||||
elif key is KeyboardKey.DOWN:
|
||||
down_path = Gtk.TreePath(paths[-1])
|
||||
set_cursor(down_path, paths, selection, view)
|
||||
down_path.next()
|
||||
if down_path < max_path:
|
||||
move_down(down_path, model, paths)
|
||||
else:
|
||||
max_path.prev()
|
||||
move_down(max_path, model, paths)
|
||||
elif key in (KeyboardKey.PAGE_UP, KeyboardKey.HOME, KeyboardKey.PAGE_UP_KP, KeyboardKey.HOME_KP):
|
||||
move_up(min_path if is_tree_store else cursor_path, model, paths)
|
||||
elif key in (KeyboardKey.PAGE_DOWN, KeyboardKey.END, KeyboardKey.END_KP, KeyboardKey.PAGE_DOWN_KP):
|
||||
move_down(max_path if is_tree_store else cursor_path, model, paths)
|
||||
|
||||
|
||||
def move_up(top_path, model, paths):
|
||||
|
||||
@@ -1894,10 +1894,12 @@ Author: Dmitriy Yefremov
|
||||
<property name="activate_on_single_click">True</property>
|
||||
<signal name="button-press-event" handler="on_view_popup_menu" object="services_popup_menu" swapped="no"/>
|
||||
<signal name="button-press-event" handler="on_view_press" swapped="no"/>
|
||||
<signal name="drag-begin" handler="on_view_drag_begin" swapped="no"/>
|
||||
<signal name="button-release-event" handler="on_view_release" swapped="no"/>
|
||||
<signal name="drag-begin" handler="on_view_drag_begin" after="yes" swapped="no"/>
|
||||
<signal name="drag-data-get" handler="on_view_drag_data_get" swapped="no"/>
|
||||
<signal name="drag-data-received" handler="on_services_view_drag_data_received" swapped="no"/>
|
||||
<signal name="drag-drop" handler="on_services_view_drag_drop" swapped="no"/>
|
||||
<signal name="drag-end" handler="on_view_drag_end" swapped="no"/>
|
||||
<signal name="focus-in-event" handler="on_view_focus" swapped="no"/>
|
||||
<signal name="key-press-event" handler="on_tree_view_key_press" swapped="no"/>
|
||||
<signal name="key-release-event" handler="on_tree_view_key_release" swapped="no"/>
|
||||
@@ -2439,9 +2441,11 @@ Author: Dmitriy Yefremov
|
||||
<property name="activate_on_single_click">True</property>
|
||||
<signal name="button-press-event" handler="on_fav_press" object="fav_popup_menu" swapped="no"/>
|
||||
<signal name="button-press-event" handler="on_view_press" swapped="yes"/>
|
||||
<signal name="drag-begin" handler="on_view_drag_begin" swapped="no"/>
|
||||
<signal name="button-release-event" handler="on_view_release" swapped="no"/>
|
||||
<signal name="drag-begin" handler="on_view_drag_begin" after="yes" swapped="no"/>
|
||||
<signal name="drag-data-get" handler="on_view_drag_data_get" swapped="no"/>
|
||||
<signal name="drag-data-received" handler="on_view_drag_data_received" swapped="no"/>
|
||||
<signal name="drag-end" handler="on_view_drag_end" swapped="no"/>
|
||||
<signal name="focus-in-event" handler="on_view_focus" swapped="no"/>
|
||||
<signal name="key-press-event" handler="on_tree_view_key_press" swapped="no"/>
|
||||
<signal name="key-release-event" handler="on_tree_view_key_release" swapped="no"/>
|
||||
@@ -2724,9 +2728,11 @@ Author: Dmitriy Yefremov
|
||||
<property name="activate_on_single_click">True</property>
|
||||
<signal name="button-press-event" handler="on_view_popup_menu" object="bouquets_popup_menu" swapped="no"/>
|
||||
<signal name="button-press-event" handler="on_view_press" swapped="yes"/>
|
||||
<signal name="drag-begin" handler="on_view_drag_begin" swapped="no"/>
|
||||
<signal name="button-release-event" handler="on_view_release" swapped="no"/>
|
||||
<signal name="drag-begin" handler="on_view_drag_begin" after="yes" swapped="no"/>
|
||||
<signal name="drag-data-get" handler="on_view_drag_data_get" swapped="no"/>
|
||||
<signal name="drag-data-received" handler="on_bq_view_drag_data_received" swapped="no"/>
|
||||
<signal name="drag-end" handler="on_view_drag_end" swapped="no"/>
|
||||
<signal name="focus-in-event" handler="on_view_focus" swapped="no"/>
|
||||
<signal name="key-press-event" handler="on_tree_view_key_press" swapped="no"/>
|
||||
<signal name="key-release-event" handler="on_tree_view_key_release" swapped="no"/>
|
||||
|
||||
Reference in New Issue
Block a user