diff --git a/app/ui/control.glade b/app/ui/control.glade
index bc9bb191..ef387699 100644
--- a/app/ui/control.glade
+++ b/app/ui/control.glade
@@ -34,21 +34,290 @@ Author: Dmitriy Yefremov
+
+
+
+
+
+
+ 59
+ 1
+ 10
+
+
+ False
+
+
+ True
+ False
+ 10
+ 10
+ 5
+ 5
+ vertical
+ 5
+
+
+ True
+ True
+ 2020
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ center
+ 2
+ 2
+
+
+ True
+ False
+ Hr.
+
+
+ 0
+ 0
+
+
+
+
+ True
+ True
+ 2
+ 2
+ number
+ end_hour_adjustment
+ True
+
+
+ 0
+ 1
+
+
+
+
+ True
+ False
+ Min.
+
+
+ 2
+ 0
+
+
+
+
+ True
+ True
+ 2
+ 2
+ number
+ min_end_adjustment
+ True
+
+
+ 2
+ 1
+
+
+
+
+ True
+ False
+ :
+
+
+ 1
+ 1
+
+
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ True
+ True
+ True
+ app.on_timer_ends_set
+ Set
+ True
+
+
+ False
+ True
+ 3
+
+
+
+
+
True
False
@@ -642,6 +911,7 @@ audio-volume-medium-symbolic
False
start
+ 5
gtk-missing-image
6
@@ -664,6 +934,7 @@ audio-volume-medium-symbolic
+ True
False
vertical
5
@@ -680,6 +951,8 @@ audio-volume-medium-symbolic
True
False
+ multiple
+ False
@@ -692,6 +965,52 @@ audio-volume-medium-symbolic
0
+
+
+ True
+ False
+ 5
+
+
+ True
+ True
+ Filter
+ gtk-spell-check
+ tools-check-spelling
+ False
+ False
+ Filter
+
+
+
+ True
+ True
+ 0
+
+
+
+
+ True
+ True
+ True
+ Add timer
+ app.on_timer_add_from_event
+ add_timer_image
+ True
+
+
+ False
+ True
+ 1
+
+
+
+
+ False
+ False
+ 1
+
+
epg
@@ -701,6 +1020,7 @@ audio-volume-medium-symbolic
+ True
False
vertical
5
@@ -717,6 +1037,10 @@ audio-volume-medium-symbolic
True
False
+ multiple
+ False
+
+
@@ -732,14 +1056,15 @@ audio-volume-medium-symbolic
True
False
+ end
True
expand
-
+
Add
- True
True
True
+ app.on_timer_add
True
@@ -748,11 +1073,11 @@ audio-volume-medium-symbolic
-
+
Remove
- True
True
True
+ app.on_timer_remove
True
@@ -761,11 +1086,11 @@ audio-volume-medium-symbolic
-
+
Edit
- True
True
True
+ app.on_timer_edit
True
@@ -776,7 +1101,7 @@ audio-volume-medium-symbolic
False
- True
+ False
2
@@ -787,6 +1112,637 @@ audio-volume-medium-symbolic
2
+
+
+ True
+ False
+ 0.5
+ in
+
+
+ True
+ False
+ 5
+ 5
+ 5
+ 5
+ vertical
+ 5
+
+
+ True
+ False
+ Timer
+
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ center
+ 5
+ 5
+ 5
+ 5
+ 5
+ 5
+
+
+ True
+ False
+ Service:
+ 0
+
+
+ 0
+ 3
+
+
+
+
+ True
+ False
+ Description:
+ 0
+
+
+ 0
+ 2
+
+
+
+
+ True
+ False
+ Name:
+ 0
+
+
+ 0
+ 1
+
+
+
+
+ True
+ True
+ gtk-edit
+
+
+ 1
+ 3
+
+
+
+
+ True
+ True
+ gtk-edit
+
+
+ 1
+ 2
+
+
+
+
+ True
+ True
+ gtk-edit
+
+
+ 1
+ 1
+
+
+
+
+ True
+ False
+ Enabled:
+ 0
+
+
+ 0
+ 0
+
+
+
+
+ True
+ True
+ end
+
+
+ 1
+ 0
+
+
+
+
+ True
+ False
+ Action:
+ 0
+
+
+ 0
+ 9
+
+
+
+
+ True
+ False
+ Repeated:
+ 0
+
+
+ 0
+ 8
+
+
+
+
+ True
+ False
+ Ends:
+ 0
+
+
+ 0
+ 7
+
+
+
+
+ True
+ False
+ Begins:
+ 0
+
+
+ 0
+ 6
+
+
+
+
+ True
+ False
+ 0
+
+ - Record
+ - Zap
+
+
+
+ 1
+ 9
+
+
+
+
+ True
+ False
+ True
+
+
+ True
+ True
+ False
+ center
+ True
+
+
+ 0
+ 1
+
+
+
+
+ True
+ True
+ False
+ center
+ True
+
+
+ 1
+ 1
+
+
+
+
+ True
+ True
+ False
+ center
+ True
+
+
+ 2
+ 1
+
+
+
+
+ True
+ True
+ False
+ center
+ True
+
+
+ 3
+ 1
+
+
+
+
+ True
+ True
+ False
+ center
+ True
+
+
+ 4
+ 1
+
+
+
+
+
+ True
+ True
+ False
+ center
+ True
+
+
+ 5
+ 1
+
+
+
+
+
+ True
+ True
+ False
+ center
+ True
+
+
+ 6
+ 1
+
+
+
+
+ True
+ False
+ Mo
+
+
+ 0
+ 0
+
+
+
+
+ True
+ False
+ Tu
+
+
+ 1
+ 0
+
+
+
+
+ True
+ False
+ We
+
+
+ 2
+ 0
+
+
+
+
+ True
+ False
+ Th
+
+
+ 3
+ 0
+
+
+
+
+ True
+ False
+ Fr
+
+
+ 4
+ 0
+
+
+
+
+ True
+ False
+ Sa
+
+
+ 5
+ 0
+
+
+
+
+ True
+ False
+ Su
+
+
+ 6
+ 0
+
+
+
+
+ 1
+ 8
+
+
+
+
+
+ 1
+ 7
+
+
+
+
+ True
+ False
+ Service reference:
+ 0
+
+
+ 0
+ 4
+
+
+
+
+ True
+ False
+ False
+
+
+ 1
+ 4
+
+
+
+
+ True
+ False
+ Event ID:
+ 0
+
+
+ 0
+ 5
+
+
+
+
+ True
+ False
+ False
+
+
+ 1
+ 5
+
+
+
+
+
+ 1
+ 6
+
+
+
+
+ True
+ False
+ After event:
+ 0
+
+
+ 0
+ 10
+
+
+
+
+ True
+ False
+ 3
+
+ - Do Nothing
+ - Standby
+ - Shut down
+ - Auto
+
+
+
+ 1
+ 10
+
+
+
+
+ True
+ False
+ end
+ 5
+
+
+ True
+ False
+ edit
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ True
+
+
+ True
+ True
+ end
+ 1
+
+
+
+
+ 1
+ 11
+
+
+
+
+ True
+ False
+ True
+ edit
+ Default
+
+
+ 1
+ 12
+
+
+
+
+ True
+ False
+ Location:
+ 0
+
+
+ 0
+ 12
+
+
+
+
+
+
+
+ True
+ True
+ 1
+
+
+
+
+ True
+ False
+ center
+ 15
+ 15
+ 5
+ 5
+ expand
+
+
+ Save
+ True
+ True
+ True
+ app.on_timer_save
+
+
+ True
+ True
+ 0
+
+
+
+
+ Cancel
+ True
+ True
+ True
+ app.on_timer_cancel
+
+
+ True
+ True
+ 1
+
+
+
+
+ False
+ True
+ end
+ 2
+
+
+
+
+
+
+
+
+
+ timer
+ 3
+
+
True
diff --git a/app/ui/control.py b/app/ui/control.py
index cd9d3c80..83fea9aa 100644
--- a/app/ui/control.py
+++ b/app/ui/control.py
@@ -2,33 +2,40 @@
import os
from datetime import datetime
from enum import Enum
+from urllib.parse import quote
from gi.repository import GLib
-from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH
+from .dialogs import get_dialogs_string, show_dialog, DialogType
+from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, Column
from ..commons import run_task, run_with_delay, log, run_idle
from ..connections import HttpAPI
class ControlBox(Gtk.HBox):
+ _TIME_STR = "%Y-%m-%d %H:%M"
+
class Tool(Enum):
""" The currently displayed tool. """
REMOTE = "control"
EPG = "epg"
TIMERS = "timers"
+ TIMER = "timer"
- class EpgRow(Gtk.HBox):
+ class EpgRow(Gtk.ListBoxRow):
def __init__(self, event: dict, **properties):
super().__init__(**properties)
- self._ev_id = event.get("e2eventid", "")
- self._ref = event.get("e2eventservicereference", "")
- self.set_orientation(Gtk.Orientation.VERTICAL)
+ self._event_data = event
+ h_box = Gtk.HBox()
+ h_box.set_orientation(Gtk.Orientation.VERTICAL)
- title_label = Gtk.Label(event.get("e2eventtitle", ""))
+ self._title = event.get("e2eventtitle", "")
+ title_label = Gtk.Label(self._title)
+ self._desc = event.get("e2eventdescription", "")
description = Gtk.Label()
- description.set_markup("{}".format(event.get("e2eventdescription", "")))
+ description.set_markup("{}".format(self._desc))
description.set_line_wrap(True)
description.set_max_width_chars(25)
@@ -37,19 +44,73 @@ class ControlBox(Gtk.HBox):
end_time = datetime.fromtimestamp(start + int(event.get("e2eventduration", "0")))
time_label = Gtk.Label()
time_label.set_margin_top(5)
- time_str = "{} - {}".format(start_time.strftime("%A, %H:%M"), end_time.strftime("%H:%M"))
- time_label.set_markup("{}".format(time_str))
+ self._time_header = "{} - {}".format(start_time.strftime("%A, %H:%M"), end_time.strftime("%H:%M"))
+ time_label.set_markup("{}".format(self._time_header))
- self.add(time_label)
- self.add(title_label)
- self.add(description)
+ h_box.add(time_label)
+ h_box.add(title_label)
+ h_box.add(description)
sep = Gtk.Separator()
sep.set_margin_top(5)
- self.add(sep)
- self.set_spacing(5)
+ h_box.add(sep)
+ h_box.set_spacing(5)
+ self.add(h_box)
self.show_all()
+ @property
+ def event_data(self):
+ return self._event_data
+
+ @property
+ def title(self):
+ return self._title
+
+ @property
+ def desc(self):
+ return self._desc
+
+ @property
+ def time_header(self):
+ return self._time_header
+
+ class TimerRow(Gtk.ListBoxRow):
+
+ _UI_PATH = UI_RESOURCES_PATH + "timer_row.glade"
+
+ def __init__(self, timer, **properties):
+ super().__init__(**properties)
+
+ self._timer = timer
+
+ builder = Gtk.Builder()
+ builder.add_from_string(get_dialogs_string(self._UI_PATH))
+ row_box = builder.get_object("timer_row_box")
+ name_label = builder.get_object("timer_name_label")
+ description_label = builder.get_object("timer_description_label")
+ service_name_label = builder.get_object("timer_service_name_label")
+ time_label = builder.get_object("timer_time_label")
+
+ name_label.set_text(timer.get("e2name", ""))
+ description_label.set_text(timer.get("e2description", ""))
+ service_name_label.set_text(timer.get("e2servicename", ""))
+ # Time
+ start_time = datetime.fromtimestamp(int(timer.get("e2timebegin", "0")))
+ end_time = datetime.fromtimestamp(int(timer.get("e2timeend", "0")))
+ time_label.set_text("{} - {}".format(start_time.strftime("%A, %H:%M"), end_time.strftime("%H:%M")))
+
+ self.add(row_box)
+ self.show()
+
+ @property
+ def timer(self):
+ return self._timer
+
+ class TimerAction(Enum):
+ ADD = 0
+ EVENT = 1
+ CHANGE = 2
+
def __init__(self, app, http_api, settings, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -57,16 +118,23 @@ class ControlBox(Gtk.HBox):
self._settings = settings
self._update_epg = False
self._app = app
+ self._last_tool = self.Tool.REMOTE
+ self._timer_action = self.TimerAction.ADD
+ self._current_timer = {}
handlers = {"on_visible_tool": self.on_visible_tool,
"on_volume_changed": self.on_volume_changed,
- "on_epg_press": self.on_epg_press}
+ "on_epg_press": self.on_epg_press,
+ "on_epg_filter_changed": self.on_epg_filter_changed,
+ "on_timers_press": self.on_timers_press,
+ "on_timers_drag_data_received": self.on_timers_drag_data_received}
builder = Gtk.Builder()
builder.add_from_file(UI_RESOURCES_PATH + "control.glade")
builder.connect_signals(handlers)
self.add(builder.get_object("main_box"))
+ self._stack = builder.get_object("stack")
self._screenshot_image = builder.get_object("screenshot_image")
self._screenshot_button_box = builder.get_object("screenshot_button_box")
self._screenshot_check_button = builder.get_object("screenshot_check_button")
@@ -76,10 +144,51 @@ class ControlBox(Gtk.HBox):
self._agc_value_label = builder.get_object("agc_value_label")
self._volume_button = builder.get_object("volume_button")
self._epg_list_box = builder.get_object("epg_list_box")
+ self._epg_list_box.set_filter_func(self.epg_filter_function)
+ self._epg_filter_entry = builder.get_object("epg_filter_entry")
self._timers_list_box = builder.get_object("timers_list_box")
self._app._control_revealer.bind_property("visible", self, "visible")
+ # Timers
+ self._timer_remove_button = builder.get_object("timer_remove_button")
+ self._timer_remove_button.bind_property("visible", builder.get_object("timer_edit_button"), "visible")
+ # Timer
+ self._timer_name_entry = builder.get_object("timer_name_entry")
+ self._timer_desc_entry = builder.get_object("timer_desc_entry")
+ self._timer_service_entry = builder.get_object("timer_service_entry")
+ self._timer_service_ref_entry = builder.get_object("timer_service_ref_entry")
+ self._timer_event_id_entry = builder.get_object("timer_event_id_entry")
+ self._timer_begins_entry = builder.get_object("timer_begins_entry")
+ self._timer_ends_entry = builder.get_object("timer_ends_entry")
+ self._timer_begins_calendar = builder.get_object("timer_begins_calendar")
+ self._timer_begins_hr_button = builder.get_object("timer_begins_hr_button")
+ self._timer_begins_min_button = builder.get_object("timer_begins_min_button")
+ self._timer_ends_calendar = builder.get_object("timer_ends_calendar")
+ self._timer_ends_hr_button = builder.get_object("timer_ends_hr_button")
+ self._timer_ends_min_button = builder.get_object("timer_ends_min_button")
+ self._timer_enabled_switch = builder.get_object("timer_enabled_switch")
+ self._timer_action_combo_box = builder.get_object("timer_action_combo_box")
+ self._timer_after_combo_box = builder.get_object("timer_after_combo_box")
+ self._timer_mo_check_button = builder.get_object("timer_mo_check_button")
+ self._timer_tu_check_button = builder.get_object("timer_tu_check_button")
+ self._timer_we_check_button = builder.get_object("timer_we_check_button")
+ self._timer_th_check_button = builder.get_object("timer_th_check_button")
+ self._timer_fr_check_button = builder.get_object("timer_fr_check_button")
+ self._timer_sa_check_button = builder.get_object("timer_sa_check_button")
+ self._timer_su_check_button = builder.get_object("timer_su_check_button")
+ self._timer_location_switch = builder.get_object("timer_location_switch")
+ self._timer_location_entry = builder.get_object("timer_location_entry")
+ self._timer_location_switch.bind_property("active", self._timer_location_entry, "sensitive")
+ # Disable DnD for timer entries.
+ self._timer_name_entry.drag_dest_unset()
+ self._timer_desc_entry.drag_dest_unset()
+ self._timer_service_entry.drag_dest_unset()
+ # DnD initialization for the timer list.
+ self._timers_list_box.drag_dest_set(Gtk.DestDefaults.ALL, [], Gdk.DragAction.DEFAULT | Gdk.DragAction.COPY)
+ self._timers_list_box.drag_dest_add_text_targets()
+
builder.get_object("stack_switcher").set_visible(settings.is_enable_experimental)
builder.get_object("epg_box").set_visible(settings.is_enable_experimental)
+ builder.get_object("timers_box").set_visible(settings.is_enable_experimental)
self.init_actions(app)
self.connect("hide", self.on_hide)
@@ -108,6 +217,15 @@ class ControlBox(Gtk.HBox):
app.set_action("on_screenshot_all", self.on_screenshot_all)
app.set_action("on_screenshot_video", self.on_screenshot_video)
app.set_action("on_screenshot_osd", self.on_screenshot_osd)
+ # Timers
+ app.set_action("on_timer_add", self.on_timer_add)
+ app.set_action("on_timer_add_from_event", self.on_timer_add_from_event)
+ app.set_action("on_timer_remove", self.on_timer_remove)
+ app.set_action("on_timer_edit", self.on_timer_edit)
+ app.set_action("on_timer_save", self.on_timer_save)
+ app.set_action("on_timer_cancel", self.on_timer_cancel)
+ app.set_action("on_timer_begins_set", self.on_timer_begins_set)
+ app.set_action("on_timer_ends_set", self.on_timer_ends_set)
@property
def update_epg(self):
@@ -120,6 +238,9 @@ class ControlBox(Gtk.HBox):
if tool is self.Tool.TIMERS:
self.update_timer_list()
+ if tool is not self.Tool.TIMER:
+ self._last_tool = tool
+
def on_hide(self, item):
self._update_epg = False
@@ -230,18 +351,330 @@ class ControlBox(Gtk.HBox):
list(map(lambda e: self._epg_list_box.add(self.EpgRow(e)), epg.get("event_list", [])))
self._app._wait_dialog.hide()
- def on_epg_press(self, list_box: Gtk.ListBox, event: Gdk.EventButton):
+ def on_epg_press(self, list_box, event):
if event.get_event_type() == Gdk.EventType.DOUBLE_BUTTON_PRESS and len(list_box) > 0:
- pass
+ row = list_box.get_selected_row()
+ if row:
+ self.set_timer_from_event_data(row.event_data)
+
+ def on_epg_filter_changed(self, entry):
+ self._epg_list_box.invalidate_filter()
+
+ def epg_filter_function(self, row: EpgRow):
+ txt = self._epg_filter_entry.get_text().upper()
+ return any((not txt, txt in row.time_header.upper(), txt in row.title.upper(), txt in row.desc.upper()))
+
+ def on_timer_add_from_event(self, action, value=None):
+ rows = self._epg_list_box.get_selected_rows()
+ if not rows:
+ self._app.show_error_dialog("No selected item!")
+ return
+
+ refs = []
+ for row in rows:
+ event = row.event_data
+ ref = "timeraddbyeventid?sRef={}&eventid={}&justplay=0".format(event.get("e2eventservicereference", ""),
+ event.get("e2eventid", ""))
+ refs.append(ref)
+
+ gen = self.write_timers_list(refs)
+ GLib.idle_add(lambda: next(gen, False))
+
+ def write_timers_list(self, refs):
+ self._app._wait_dialog.show()
+ tasks = list(refs)
+ for ref in refs:
+ self._http_api.send(HttpAPI.Request.TIMER, ref, lambda x: tasks.pop())
+ yield True
+
+ while tasks:
+ yield True
+
+ self._stack.set_visible_child_name(self.Tool.TIMERS.value)
# *********************** Timers *************************** #
+ def on_timers_press(self, list_box, event):
+ if event.get_event_type() == Gdk.EventType.DOUBLE_BUTTON_PRESS and len(list_box) > 0:
+ self.on_timer_edit()
+
def update_timer_list(self):
self._app._wait_dialog.show()
self._http_api.send(HttpAPI.Request.TIMER_LIST, "", self.update_timers_data)
@run_idle
def update_timers_data(self, timers):
- timers = timers.get("timer_list", [])
list(map(self._timers_list_box.remove, (r for r in self._timers_list_box)))
+ list(map(lambda t: self._timers_list_box.add(self.TimerRow(t)), timers.get("timer_list", [])))
+ self._timer_remove_button.set_visible(len(self._timers_list_box))
self._app._wait_dialog.hide()
+
+ def on_timer_add(self, action=None, value=None):
+ self._timer_action = self.TimerAction.ADD
+ date = datetime.now()
+ self.set_begins_date(date)
+ self.set_ends_date(date)
+ self._timer_event_id_entry.set_text("")
+ self._timer_location_switch.set_active(False)
+ self.set_repetition_flags(0)
+ self._stack.set_visible_child_name(self.Tool.TIMER.value)
+
+ def on_timer_remove(self, action, value=None):
+ rows = self._timers_list_box.get_selected_rows()
+ if not rows or show_dialog(DialogType.QUESTION, self._app._main_window) != Gtk.ResponseType.OK:
+ return
+
+ refs = {}
+ for row in rows:
+ timer = row.timer
+ ref = "timerdelete?sRef={}&begin={}&end={}".format(timer.get("e2servicereference", ""),
+ timer.get("e2timebegin", ""),
+ timer.get("e2timeend", ""))
+ refs[ref] = row
+
+ self._app._wait_dialog.show("Deleting data...")
+ gen = self.remove_timers(refs)
+ GLib.idle_add(lambda: next(gen, False))
+
+ def remove_timers(self, refs):
+ tasks = list(refs)
+ removed = set()
+ for ref in refs:
+ yield from self.remove_timer(ref, removed, tasks)
+
+ while tasks:
+ yield True
+
+ list(map(self._timers_list_box.remove, (refs[ref] for ref in refs if ref in removed)))
+ self._app._wait_dialog.hide()
+ self._timer_remove_button.set_visible(len(self._timers_list_box))
+ yield True
+
+ def remove_timer(self, ref, removed, tasks=None):
+ def callback(resp):
+ if resp.get("e2state", "") == "True":
+ log(resp.get("e2statetext", ""))
+ removed.add(ref)
+ else:
+ log(resp.get("e2statetext", None) or "Timer deletion error.")
+ if tasks:
+ tasks.pop()
+
+ self._http_api.send(HttpAPI.Request.TIMER, ref, callback)
+ yield True
+
+ def on_timer_edit(self, action=None, value=None):
+ row = self._timers_list_box.get_selected_row()
+ if row:
+ self._timer_action = self.TimerAction.CHANGE
+
+ timer = row.timer
+ self._current_timer = timer
+ self._timer_name_entry.set_text(timer.get("e2name", ""))
+ self._timer_desc_entry.set_text(timer.get("e2description", ""))
+ self._timer_service_entry.set_text(timer.get("e2servicename", ""))
+ self._timer_service_ref_entry.set_text(timer.get("e2servicereference", ""))
+ self._timer_event_id_entry.set_text(timer.get("e2eit", ""))
+ self._timer_enabled_switch.set_active((timer.get("e2disabled", "0") == "0"))
+ self._timer_action_combo_box.set_active_id(timer.get("e2justplay", "0"))
+ self._timer_after_combo_box.set_active_id(timer.get("e2afterevent", "0"))
+ self.set_time_data(int(timer.get("e2timebegin", "0")), int(timer.get("e2timeend", "0")))
+ location = timer.get("e2location", "")
+ self._timer_location_entry.set_text("" if location == "None" else location)
+ # Days
+ self.set_repetition_flags(int(timer.get("e2repeated", "0")))
+ self._stack.set_visible_child_name(self.Tool.TIMER.value)
+
+ def on_timer_save(self, action, value=None):
+ args = []
+ t_data = self.get_timer_data()
+ s_ref = t_data.get("sRef", "")
+
+ if self._timer_action is self.TimerAction.EVENT:
+ args.append("timeraddbyeventid?sRef={}".format(s_ref))
+ args.append("eventid={}".format(t_data.get("eit", "0")))
+ args.append("justplay={}".format(t_data.get("justplay", "")))
+ args.append("tags={}".format(""))
+ else:
+ if self._timer_action is self.TimerAction.ADD:
+ args.append("timeradd?sRef={}".format(s_ref))
+ args.append("deleteOldOnSave={}".format(0))
+ elif self._timer_action is self.TimerAction.CHANGE:
+ args.append("timerchange?sRef={}".format(s_ref))
+ args.append("channelOld={}".format(s_ref))
+ args.append("beginOld={}".format(self._current_timer.get("e2timebegin", "0")))
+ args.append("endOld={}".format(self._current_timer.get("e2timeend", "0")))
+ args.append("deleteOldOnSave={}".format(1))
+
+ args.append("begin={}".format(t_data.get("begin", "")))
+ args.append("end={}".format(t_data.get("end", "")))
+ args.append("name={}".format(quote(t_data.get("name", ""))))
+ args.append("description={}".format(quote(t_data.get("description", ""))))
+ args.append("tags={}".format(""))
+ args.append("eit={}".format("0"))
+ args.append("disabled={}".format(t_data.get("disabled", "1")))
+ args.append("justplay={}".format(t_data.get("justplay", "1")))
+ args.append("afterevent={}".format(t_data.get("afterevent", "0")))
+ args.append("repeated={}".format(self.get_repetition_flags()))
+
+ if self._timer_location_switch.get_active():
+ args.append("dirname={}".format(self._timer_location_entry.get_text()))
+
+ self._http_api.send(HttpAPI.Request.TIMER, "&".join(args), self.timer_add_edit_callback)
+
+ @run_idle
+ def timer_add_edit_callback(self, resp):
+ if "error_code" in resp:
+ msg = "Error getting timer status.\n{}".format(resp.get("error_code"))
+ self._app.show_error_dialog(msg)
+ log(msg)
+ return
+
+ state = resp.get("e2state", None)
+ if state == "False":
+ msg = resp.get("e2statetext", "")
+ self._app.show_error_dialog(msg)
+ log(msg)
+ if state == "True":
+ log(resp.get("e2statetext", ""))
+ self._stack.set_visible_child_name(self._last_tool.value)
+ else:
+ log("Error getting timer status. No response!")
+
+ def on_timer_cancel(self, action, value=None):
+ self._stack.set_visible_child_name(self._last_tool.value)
+
+ def on_timer_begins_set(self, action, value=None):
+ self.set_begins_date(self.get_begins_date())
+
+ def on_timer_ends_set(self, action, value=None):
+ self.set_ends_date(self.get_ends_date())
+
+ def get_begins_date(self):
+ date = self._timer_begins_calendar.get_date()
+ return datetime(year=date.year, month=date.month + 1, day=date.day,
+ hour=int(self._timer_begins_hr_button.get_value()),
+ minute=int(self._timer_begins_min_button.get_value()))
+
+ def set_begins_date(self, date):
+ hour = date.hour
+ minute = date.minute
+ self._timer_begins_hr_button.set_value(hour)
+ self._timer_begins_min_button.set_value(minute)
+ self._timer_begins_calendar.select_day(date.day)
+ self._timer_begins_calendar.select_month(date.month - 1, date.year)
+ self._timer_begins_entry.set_text("{}-{}-{} {}:{:02d}".format(date.year, date.month, date.day, hour, minute))
+
+ def get_ends_date(self):
+ date = self._timer_ends_calendar.get_date()
+ return datetime(year=date.year, month=date.month + 1, day=date.day,
+ hour=int(self._timer_ends_hr_button.get_value()),
+ minute=int(self._timer_ends_min_button.get_value()))
+
+ def set_ends_date(self, date):
+ hour = date.hour
+ minute = date.minute
+ self._timer_ends_hr_button.set_value(hour)
+ self._timer_ends_min_button.set_value(minute)
+ self._timer_ends_calendar.select_day(date.day)
+ self._timer_ends_calendar.select_month(date.month - 1, date.year)
+ self._timer_ends_entry.set_text("{}-{}-{} {}:{:02d}".format(date.year, date.month, date.day, hour, minute))
+
+ def set_timer_from_event_data(self, timer):
+ self._stack.set_visible_child_name(self.Tool.TIMER.value)
+ self._timer_action = self.TimerAction.EVENT
+ self._timer_name_entry.set_text(timer.get("e2eventtitle", ""))
+ self._timer_desc_entry.set_text(timer.get("e2eventdescription", ""))
+ self._timer_service_entry.set_text(timer.get("e2eventservicename", ""))
+ self._timer_service_ref_entry.set_text(timer.get("e2eventservicereference", ""))
+ self._timer_event_id_entry.set_text(timer.get("e2eventid", ""))
+ self._timer_action_combo_box.set_active_id("1")
+ self._timer_after_combo_box.set_active_id("3")
+ start_time = int(timer.get("e2eventstart", "0"))
+ self.set_time_data(start_time, start_time + int(timer.get("e2eventduration", "0")))
+
+ def set_time_data(self, start_time, end_time):
+ """ Sets values for time widgets. """
+ ev_time_start = datetime.fromtimestamp(start_time) or datetime.now()
+ ev_time_end = datetime.fromtimestamp(end_time) or datetime.now()
+ self._timer_begins_entry.set_text(ev_time_start.strftime(self._TIME_STR))
+ self._timer_ends_entry.set_text(ev_time_end.strftime(self._TIME_STR))
+ self._timer_begins_calendar.select_day(ev_time_start.day)
+ self._timer_begins_calendar.select_month(ev_time_start.month - 1, ev_time_start.year)
+ self._timer_ends_calendar.select_day(ev_time_end.day)
+ self._timer_ends_calendar.select_month(ev_time_end.month - 1, ev_time_end.year)
+ self._timer_begins_hr_button.set_value(ev_time_start.hour)
+ self._timer_begins_min_button.set_value(ev_time_start.minute)
+ self._timer_ends_hr_button.set_value(ev_time_end.hour)
+ self._timer_ends_min_button.set_value(ev_time_end.minute)
+
+ def get_timer_data(self):
+ """ Returns timer data as a dict. """
+ return {"sRef": self._timer_service_ref_entry.get_text(),
+ "begin": int(datetime.strptime(self._timer_begins_entry.get_text(), self._TIME_STR).timestamp()),
+ "end": int(datetime.strptime(self._timer_ends_entry.get_text(), self._TIME_STR).timestamp()),
+ "name": self._timer_name_entry.get_text(),
+ "description": self._timer_desc_entry.get_text(),
+ "dirname": "",
+ "eit": self._timer_event_id_entry.get_text(),
+ "disabled": int(not self._timer_enabled_switch.get_active()),
+ "justplay": self._timer_action_combo_box.get_active_id(),
+ "afterevent": self._timer_after_combo_box.get_active_id(),
+ "repeated": self.get_repetition_flags()}
+
+ def get_repetition_flags(self):
+ """ Returns flags for repetition. """
+ day_flags = 0
+ for i, box in enumerate((self._timer_mo_check_button,
+ self._timer_tu_check_button,
+ self._timer_we_check_button,
+ self._timer_th_check_button,
+ self._timer_fr_check_button,
+ self._timer_sa_check_button,
+ self._timer_su_check_button)):
+
+ if box.get_active():
+ day_flags = day_flags | (1 << i)
+
+ return day_flags
+
+ def set_repetition_flags(self, flags):
+ for i, box in enumerate((self._timer_mo_check_button,
+ self._timer_tu_check_button,
+ self._timer_we_check_button,
+ self._timer_th_check_button,
+ self._timer_fr_check_button,
+ self._timer_sa_check_button,
+ self._timer_su_check_button)):
+ box.set_active(flags & 1 == 1)
+ flags = flags >> 1
+
+ # ***************** Drag-and-drop ********************* #
+
+ def on_timers_drag_data_received(self, box, context, x, y, data, info, time):
+ txt = data.get_text()
+ if txt:
+ itr_str, sep, source = txt.partition(self._app.DRAG_SEP)
+ if not source:
+ return
+
+ itrs = itr_str.split(",")
+ if len(itrs) > 1:
+ self._app.show_error_dialog("Please, select only one item!")
+ return
+
+ fav_id = None
+ if source == self._app.FAV_MODEL_NAME:
+ model = self._app.fav_view.get_model()
+ fav_id = model.get_value(model.get_iter_from_string(itrs[0]), Column.FAV_ID)
+ elif source == self._app.SERVICE_MODEL_NAME:
+ model = self._app.services_view.get_model()
+ fav_id = model.get_value(model.get_iter_from_string(itrs[0]), Column.SRV_FAV_ID)
+
+ service = self._app.current_services.get(fav_id, None)
+ if service:
+ self._timer_name_entry.set_text(service.service)
+ self._timer_service_entry.set_text(service.service)
+ self._timer_service_ref_entry.set_text(service.picon_id.rstrip(".png").replace("_", ":"))
+ self.on_timer_add()
+ context.finish(True, False, time)
diff --git a/app/ui/main_app_window.py b/app/ui/main_app_window.py
index 213e8b14..282a8adf 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"
+ DRAG_SEP = "::::"
DEL_FACTOR = 50 # Batch size to delete in one pass.
FAV_FACTOR = DEL_FACTOR * 2
@@ -496,7 +497,7 @@ class Application(Gtk.Application):
self._services_view.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, target, Gdk.DragAction.COPY)
self._services_view.enable_model_drag_dest([], Gdk.DragAction.DEFAULT | Gdk.DragAction.MOVE)
self._fav_view.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, target,
- Gdk.DragAction.DEFAULT | Gdk.DragAction.MOVE)
+ Gdk.DragAction.DEFAULT | Gdk.DragAction.MOVE | Gdk.DragAction.COPY)
self._fav_view.enable_model_drag_dest(target, Gdk.DragAction.DEFAULT | Gdk.DragAction.MOVE)
self._bouquets_view.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, bq_target,
Gdk.DragAction.DEFAULT | Gdk.DragAction.MOVE)
@@ -1170,7 +1171,7 @@ class Application(Gtk.Application):
self.on_import_bouquet(None, file_path=urlparse(unquote(data)).path.strip())
return
- itr_str, sep, source = data.partition("::::")
+ itr_str, sep, source = data.partition(self.DRAG_SEP)
if source != self.BQ_MODEL_NAME:
return
@@ -1216,7 +1217,7 @@ class Application(Gtk.Application):
def receive_selection(self, *, view, drop_info, data):
""" Update fav view after data received """
try:
- itr_str, sep, source = data.partition("::::")
+ itr_str, sep, source = data.partition(self.DRAG_SEP)
if source == self.BQ_MODEL_NAME:
return
diff --git a/app/ui/timer_row.glade b/app/ui/timer_row.glade
new file mode 100644
index 00000000..aea8825c
--- /dev/null
+++ b/app/ui/timer_row.glade
@@ -0,0 +1,141 @@
+
+
+
+
+
+ True
+ False
+ 2
+ 2
+ vertical
+ 5
+
+
+ True
+ False
+ 5
+
+
+ True
+ False
+ end
+
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+
+
+ False
+ True
+ end
+ 1
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ 5
+
+
+ True
+ False
+ end
+
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+
+
+ False
+ True
+ end
+ 1
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ True
+ False
+ 5
+
+
+ True
+ False
+ end
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+
+
+
+
+
+ False
+ True
+ end
+ 1
+
+
+
+
+ False
+ True
+ 2
+
+
+
+
+ True
+ False
+
+
+ False
+ True
+ 3
+
+
+
+