diff --git a/app/ui/main_app_window.py b/app/ui/main_app_window.py index cfce2867..d9426534 100644 --- a/app/ui/main_app_window.py +++ b/app/ui/main_app_window.py @@ -43,6 +43,7 @@ class Application(Gtk.Application): SERVICE_MODEL_NAME = "services_list_store" FAV_MODEL_NAME = "fav_list_store" BQ_MODEL_NAME = "bouquets_tree_store" + ALT_MODEL_NAME = "alt_list_store" DRAG_SEP = "::::" DEL_FACTOR = 50 # Batch size to delete in one pass. @@ -58,7 +59,7 @@ class Application(Gtk.Application): _FAV_ELEMENTS = ("fav_cut_popup_item", "fav_paste_popup_item", "fav_locate_popup_item", "fav_iptv_popup_item", "fav_insert_marker_popup_item", "fav_insert_space_popup_item", "fav_edit_sub_menu_popup_item", "fav_edit_popup_item", "fav_picon_popup_item", "fav_copy_popup_item", - "fav_epg_configuration_popup_item") + "fav_epg_configuration_popup_item", "fav_add_alt_popup_item") _BOUQUET_ELEMENTS = ("bouquets_new_popup_item", "bouquets_edit_popup_item", "bouquets_cut_popup_item", "bouquets_copy_popup_item", "bouquets_paste_popup_item", "new_header_button", @@ -116,6 +117,7 @@ class Application(Gtk.Application): "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_alt_view_drag_data_received": self.on_alt_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, @@ -166,7 +168,8 @@ class Application(Gtk.Application): "on_create_bouquet_for_current_package": self.on_create_bouquet_for_current_package, "on_create_bouquet_for_each_package": self.on_create_bouquet_for_each_package, "on_create_bouquet_for_current_type": self.on_create_bouquet_for_current_type, - "on_create_bouquet_for_each_type": self.on_create_bouquet_for_each_type} + "on_create_bouquet_for_each_type": self.on_create_bouquet_for_each_type, + "on_add_alternatives": self.on_add_alternatives} self._settings = Settings.get_instance() self._s_type = self._settings.setting_type @@ -254,6 +257,7 @@ class Application(Gtk.Application): self._receiver_info_box.bind_property("visible", self._http_status_image, "visible", 4) self._receiver_info_box.bind_property("visible", self._signal_box, "visible") # Alternatives + self._alt_view = builder.get_object("alt_tree_view") self._alt_model = builder.get_object("alt_list_store") self._alt_revealer = builder.get_object("alt_revealer") self._alt_revealer.bind_property("visible", self._alt_revealer, "reveal-child") @@ -461,6 +465,7 @@ class Application(Gtk.Application): self._bouquets_view.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, bq_target, Gdk.DragAction.DEFAULT | Gdk.DragAction.MOVE) self._bouquets_view.enable_model_drag_dest(bq_target, Gdk.DragAction.DEFAULT | Gdk.DragAction.MOVE) + self._alt_view.enable_model_drag_dest(bq_target, Gdk.DragAction.DEFAULT | Gdk.DragAction.COPY) self._fav_view.drag_source_set_target_list(None) self._fav_view.drag_dest_add_text_targets() @@ -477,6 +482,7 @@ class Application(Gtk.Application): self._bouquets_view.drag_dest_add_text_targets() self._bouquets_view.drag_source_add_text_targets() + self._alt_view.drag_dest_add_text_targets() 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. @@ -679,6 +685,8 @@ class Application(Gtk.Application): priority = GLib.PRIORITY_DEFAULT elif model_name == self.SERVICE_MODEL_NAME: gen = self.delete_services(itrs, model, rows) + elif model_name == self.ALT_MODEL_NAME: + gen = self.delete_alts(itrs, model, rows) GLib.idle_add(lambda: next(gen, False), priority=priority) self.on_view_focus(view) @@ -1682,13 +1690,15 @@ class Application(Gtk.Application): row = model[path][:] if row[Column.FAV_TYPE] == BqServiceType.ALT.name: self._alt_model.clear() - srv = self._services.get(row[Column.FAV_ID], None) + a_id = row[Column.FAV_ID] + srv = self._services.get(a_id, None) if srv: - for index, s in enumerate(srv[-1] or [], start=1): + for i, s in enumerate(srv[-1] or [], start=1): srv = self._services.get(s.data, None) if srv: - picon = self._picons.get(srv.picon_id, None) - self._alt_model.append((index, picon, srv.service, srv.service_type, srv.pos)) + pic = self._picons.get(srv.picon_id, None) + itr = model.get_string_from_iter(model.get_iter(path)) + self._alt_model.append((i, pic, srv.service, srv.service_type, srv.pos, srv.fav_id, a_id, itr)) self._alt_revealer.set_visible(True) else: self._alt_revealer.set_visible(False) @@ -2998,7 +3008,7 @@ class Application(Gtk.Application): def get_target_view(self, view): return ViewTarget.SERVICES if Gtk.Buildable.get_name(view) == "services_tree_view" else ViewTarget.FAV - # ***************** Bouquets *********************# + # ***************** Bouquets ********************* # def on_create_bouquet_for_current_satellite(self, item): self.create_bouquets(BqGenType.SAT) @@ -3022,7 +3032,114 @@ class Application(Gtk.Application): gen_bouquets(self._services_view, self._bouquets_view, self._main_window, g_type, self._TV_TYPES, self._s_type, self.append_bouquet) - # ***************** Profile label *********************# + # ***************** Alternatives ********************* # + + def on_add_alternatives(self, item): + model, paths = self._fav_view.get_selection().get_selected_rows() + if not paths: + return + + if len(paths) > 1: + self.show_error_dialog("Please, select only one item!") + return + + row = model[paths][:] + if row[Column.FAV_TYPE] in {BqServiceType.MARKER.name, BqServiceType.SPACE.name, BqServiceType.ALT.name}: + self.show_error_dialog("Operation not allowed in this context!") + return + + srv = self._services.get(row[Column.FAV_ID], None) + bq = self._bouquets.get(self._bq_selected, None) + if not srv or not bq: + return + + fav_id = srv.fav_id + alt_id = "alternatives_{}_{}".format(self._bq_selected, fav_id) + if alt_id in bq: + self.show_error_dialog("A similar service is already in this list!") + return + + dt, it = BqServiceType.DEFAULT, BqServiceType.IPTV + bq_srv = BouquetService(None, dt if srv.service_type != it.name else it, fav_id, 0) + s_type = BqServiceType.ALT.name + a_srv = srv._replace(service_type=s_type, pos=None, fav_id=alt_id, data_id=alt_id, transponder=(bq_srv,)) + try: + index = bq.index(fav_id) + except ValueError as e: + log("[on_add_alternatives] error: {}".format(e)) + else: + bq[index] = alt_id + self._services[alt_id] = a_srv + data = {Column.FAV_CODED: srv.coded, Column.FAV_SERVICE: srv.service, Column.FAV_LOCKED: srv.locked, + Column.FAV_HIDE: srv.hide, Column.FAV_TYPE: s_type, Column.FAV_POS: None, + Column.FAV_ID: alt_id, Column.FAV_PICON: self._picons.get(srv.picon_id, None)} + model.set(model.get_iter(paths), data) + self._fav_view.row_activated(paths[0], self._fav_view.get_column(Column.FAV_NUM)) + + def delete_alts(self, itrs, model, rows): + list(map(model.remove, itrs)) + row = rows[0] + alt_id = row[Column.ALT_ID] + + if not len(model): + bq = self._bouquets.get(self._bq_selected, None) + if not bq: + return + + fav_id, itr = row[Column.ALT_FAV_ID], row[Column.ALT_ITER] + bq[bq.index(alt_id)] = fav_id + self._services.pop(alt_id, None) + srv = self._services.get(fav_id, None) + if srv: + itr = self._fav_model.get_iter_from_string(itr) + data = {Column.FAV_CODED: srv.coded, Column.FAV_SERVICE: srv.service, Column.FAV_LOCKED: srv.locked, + Column.FAV_HIDE: srv.hide, Column.FAV_TYPE: srv.service_type, Column.FAV_POS: srv.pos, + Column.FAV_ID: srv.fav_id, Column.FAV_PICON: self._picons.get(srv.picon_id, None)} + self._fav_model.set(itr, data) + self._alt_revealer.set_visible(False) + else: + srv = self._services.get(alt_id, None) + if srv: + alt_services = srv.transponder or () + alt_services = tuple(s for s in alt_services if s.data not in {row[Column.ALT_FAV_ID] for row in rows}) + self._services[alt_id] = srv._replace(transponder=alt_services) + + yield True + + def on_alt_view_drag_data_received(self, view, drag_context, x, y, data, info, time): + txt = data.get_text() + if txt: + itr_str, sep, source = txt.partition(self.DRAG_SEP) + if source == self.SERVICE_MODEL_NAME: + model, id_col, t_col = self._services_view.get_model(), Column.SRV_FAV_ID, Column.SRV_TYPE + elif source == self.FAV_MODEL_NAME: + model, id_col, t_col = self._fav_view.get_model(), Column.FAV_ID, Column.FAV_TYPE + else: + return True + + srv = self._services.get(self._alt_model.get_value(self._alt_model.get_iter_first(), Column.ALT_ID), None) + if not srv: + return True + + itrs = tuple(model.get_iter_from_string(itr) for itr in itr_str.split(",")) + types = {BqServiceType.MARKER.name, BqServiceType.SPACE.name, BqServiceType.ALT.name} + ids = tuple(model.get_value(itr, id_col) for itr in itrs if model.get_value(itr, t_col) not in types) + srvs = tuple(self._services.get(f_id, None) for f_id in ids) + dt, it = BqServiceType.DEFAULT, BqServiceType.IPTV + a_srvs = tuple(BouquetService(None, dt if s.service_type != it.name else it, s.fav_id, 0) for s in srvs) + alt_services = srv.transponder + a_srvs + self._services[srv.fav_id] = srv._replace(transponder=alt_services) + + a_row = self._alt_model[self._alt_model.get_iter_first()][:] + alt_id, a_itr = a_row[Column.ALT_ID], a_row[Column.ALT_ITER] + + for i, srv in enumerate(srvs, start=len(self._alt_model) + 1): + pic = self._picons.get(srv.picon_id, None) + self._alt_model.append((i, pic, srv.service, srv.service_type, srv.pos, srv.fav_id, alt_id, a_itr)) + + return True + + # ***************** Profile label ********************* # @run_idle def update_profile_label(self): diff --git a/app/ui/main_window.glade b/app/ui/main_window.glade index 38cd6716..0430df52 100644 --- a/app/ui/main_window.glade +++ b/app/ui/main_window.glade @@ -1,5 +1,5 @@ - + + True + False + list-add + @@ -46,8 +51,28 @@ Author: Dmitriy Yefremov + + + + + + + + True + False + + + gtk-remove + True + False + True + True + + + + @@ -2472,10 +2497,14 @@ Author: Dmitriy Yefremov - - 25 - Picon + + True + 50 + Service + True + True 0.5 + 2 @@ -2485,17 +2514,6 @@ Author: Dmitriy Yefremov 8 - - - - - True - 50 - Service - True - True - 0.5 - 2 @@ -2647,9 +2665,14 @@ Author: Dmitriy Yefremov True alt_list_store False + True both + + - + + multiple + @@ -2669,9 +2692,11 @@ Author: Dmitriy Yefremov - - 25 - Picon + + True + 50 + Service + True 0.5 @@ -2681,15 +2706,6 @@ Author: Dmitriy Yefremov 1 - - - - - True - 50 - Service - True - 0.5 0.10000000149011612 @@ -2731,6 +2747,30 @@ Author: Dmitriy Yefremov + + + False + ID + + + + 5 + + + + + + 6 + + + + + + 7 + + + + @@ -3477,6 +3517,23 @@ Author: Dmitriy Yefremov + + + True + False + + + + + Add alternatives + True + False + False + alt_image + False + + + True diff --git a/app/ui/uicommons.py b/app/ui/uicommons.py index e8512b5e..9acd13de 100644 --- a/app/ui/uicommons.py +++ b/app/ui/uicommons.py @@ -157,7 +157,7 @@ class BqGenType(Enum): class Column(IntEnum): """ Column nums in the views """ - # main view + # Main view SRV_CAS_FLAGS = 0 SRV_STANDARD = 1 SRV_CODED = 2 @@ -180,7 +180,7 @@ class Column(IntEnum): SRV_TRANSPONDER = 19 SRV_TOOLTIP = 20 SRV_BACKGROUND = 21 - # fav view + # FAV view FAV_NUM = 0 FAV_CODED = 1 FAV_SERVICE = 2 @@ -192,11 +192,20 @@ class Column(IntEnum): FAV_PICON = 8 FAV_TOOLTIP = 9 FAV_BACKGROUND = 10 - # bouquets view + # Bouquets view BQ_NAME = 0 BQ_LOCKED = 1 BQ_HIDDEN = 2 BQ_TYPE = 3 + # Alternatives view + ALT_NUM = 0 + ALT_PICON = 1 + ALT_SERVICE = 2 + ALT_TYPE = 3 + ALT_POS = 4 + ALT_FAV_ID = 5 + ALT_ID = 6 + ALT_ITER = 7 def __index__(self): """ Overridden to get the index in slices directly """