348 lines
9.7 KiB
Python
348 lines
9.7 KiB
Python
#
|
|
# Epour - A bittorrent client using EFL and libtorrent
|
|
#
|
|
# Copyright 2012-2017 Kai Huuhko <kai.huuhko@gmail.com>
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
# MA 02110-1301, USA.
|
|
#
|
|
|
|
from efl.evas import EVAS_HINT_EXPAND, EVAS_HINT_FILL, Rectangle
|
|
from efl.ecore import Timer
|
|
from efl.elementary import Box
|
|
from efl.elementary import Spinner
|
|
from efl.elementary import Hoversel
|
|
from efl.elementary import Label
|
|
from efl.elementary import Notify
|
|
from efl.elementary import Popup
|
|
from efl.elementary import Button
|
|
from efl.elementary import Grid
|
|
from efl.elementary import Fileselector
|
|
from efl.elementary import FileselectorButton
|
|
|
|
EXPAND_BOTH = EVAS_HINT_EXPAND, EVAS_HINT_EXPAND
|
|
EXPAND_HORIZ = EVAS_HINT_EXPAND, 0.0
|
|
FILL_BOTH = EVAS_HINT_FILL, EVAS_HINT_FILL
|
|
FILL_HORIZ = EVAS_HINT_FILL, 0.5
|
|
|
|
if "xrange" not in dir(__builtins__):
|
|
xrange = range
|
|
|
|
|
|
def chunker(seq, size):
|
|
return (seq[pos:pos + size] for pos in xrange(0, len(seq), size))
|
|
|
|
|
|
class UnitSpinner(Box):
|
|
|
|
si_prefixes = ("", "k", "M", "G", "T", "P")
|
|
binary_prefixes = ("", "Ki", "Mi", "Gi", "Ti", "Pi")
|
|
|
|
def __init__(self, parent, unit, base=1000, prefixes=si_prefixes):
|
|
|
|
self.unit = unit
|
|
self.base = base
|
|
self.prefixes = prefixes
|
|
|
|
super(UnitSpinner, self).__init__(parent)
|
|
self.horizontal = True
|
|
|
|
self.save_timer = None
|
|
|
|
s = self.spinner = Spinner(parent)
|
|
s.size_hint_weight = EXPAND_HORIZ
|
|
s.size_hint_align = FILL_HORIZ
|
|
s.min_max = 0, base
|
|
s.show()
|
|
self.pack_end(s)
|
|
|
|
hs = self.hoversel = Hoversel(parent)
|
|
hs.hover_parent = parent.top_widget
|
|
for p in prefixes:
|
|
hs.item_add(p + unit)
|
|
hs.show()
|
|
self.pack_end(hs)
|
|
|
|
hs.callback_selected_add(lambda x, y: x.text_set(y.text))
|
|
|
|
def callback_changed_add(self, func, delay=None):
|
|
self.spinner.callback_changed_add(self.changed_cb, func, delay)
|
|
self.hoversel.callback_selected_add(self.changed_cb, func, delay)
|
|
|
|
def changed_cb(self, widget, *args):
|
|
func, delay = args[-2:]
|
|
|
|
if delay:
|
|
if self.save_timer is not None:
|
|
self.save_timer.delete()
|
|
|
|
self.save_timer = Timer(2.0, self.save_cb, func)
|
|
else:
|
|
self.save_cb(func)
|
|
|
|
def save_cb(self, func):
|
|
v = int(self.get_value())
|
|
func(self, v)
|
|
self.save_timer = None
|
|
return False
|
|
|
|
def get_value(self):
|
|
p = self.hoversel.text[:-len(self.unit)]
|
|
return self.spinner.value * (
|
|
self.base ** self.prefixes.index(p)
|
|
)
|
|
|
|
def set_value(self, v):
|
|
i = 0
|
|
|
|
while v // self.base > 0:
|
|
i += 1
|
|
v = float(v) / float(self.base)
|
|
|
|
if i > len(self.prefixes):
|
|
i = -1
|
|
|
|
self.spinner.value = v
|
|
self.hoversel.text = self.prefixes[i] + self.unit
|
|
|
|
|
|
class Information(Notify):
|
|
def __init__(self, parent, text, button=False, timeout=3, *args, **kwargs):
|
|
super(self.__class__, self).__init__(parent)
|
|
b = Box(self)
|
|
l = Label(b, text=text)
|
|
b.pack_end(l)
|
|
l.show()
|
|
self.content = l
|
|
if button:
|
|
xbtn = Button(b, text=_("Close"))
|
|
xbtn.callback_clicked_add(lambda x: self.delete())
|
|
b.pack_end(xbtn)
|
|
self.timeout = timeout
|
|
self.show()
|
|
|
|
|
|
class Error(Popup):
|
|
def __init__(self, parent, title, text):
|
|
super(self.__class__, self).__init__(parent)
|
|
self.part_text_set("title,text", title)
|
|
self.text = text
|
|
b = Button(self)
|
|
b.text = _("OK")
|
|
b.callback_clicked_add(lambda x: self.delete())
|
|
self.part_content_set("button1", b)
|
|
self.show()
|
|
|
|
|
|
class ConfirmExit(Popup):
|
|
def __init__(self, parent, exit_func):
|
|
super(self.__class__, self).__init__(parent)
|
|
self.part_text_set("title,text", _("Confirm exit"))
|
|
self.text = _("Are you sure you wish to exit Epour?")
|
|
b = Button(self)
|
|
b.text = _("Yes")
|
|
b.callback_clicked_add(lambda x: exit_func())
|
|
self.part_content_set("button1", b)
|
|
b = Button(self)
|
|
b.text = _("No")
|
|
b.callback_clicked_add(lambda x: self.delete())
|
|
self.part_content_set("button2", b)
|
|
self.show()
|
|
|
|
|
|
class BlockGraph(Grid):
|
|
|
|
colors = (
|
|
(255, 0, 0, 255),
|
|
(255, 255, 0, 255),
|
|
(0, 255, 0, 255)
|
|
)
|
|
|
|
def __init__(self, parent, pieces, num_completed_pieces=0, *args, **kwargs):
|
|
|
|
self.num_completed_pieces = num_completed_pieces
|
|
self.pieces = pieces
|
|
|
|
self.rects = []
|
|
|
|
super(self.__class__, self).__init__(parent, *args, **kwargs)
|
|
|
|
self.pack_rects()
|
|
|
|
@property
|
|
def num_total_pieces(self):
|
|
return len(self.pieces)
|
|
|
|
@property
|
|
def block_size(self):
|
|
width, height = self.size
|
|
num_pieces = self.num_total_pieces
|
|
grid_size = width * height
|
|
block_size = \
|
|
num_pieces//grid_size + bool(num_pieces % grid_size)
|
|
|
|
return block_size
|
|
|
|
@property
|
|
def blocks(self):
|
|
blocks = []
|
|
|
|
if self.block_size == 0:
|
|
raise ValueError("Block size 0")
|
|
|
|
for block in chunker(self.pieces, self.block_size):
|
|
if all(block):
|
|
blocks.append(2)
|
|
elif any(block):
|
|
blocks.append(1)
|
|
else:
|
|
blocks.append(0)
|
|
|
|
return blocks
|
|
|
|
def pack_rects(self):
|
|
if self.num_total_pieces == 0:
|
|
return
|
|
blocks = self.blocks
|
|
width, height = self.size
|
|
len_blocks = len(blocks)
|
|
|
|
p = 0
|
|
for y in xrange(height):
|
|
for x in xrange(width):
|
|
if p >= len_blocks:
|
|
continue
|
|
else:
|
|
block = blocks[p]
|
|
if block == 0:
|
|
color = self.colors[0]
|
|
elif block == 1:
|
|
color = self.colors[1]
|
|
else:
|
|
color = self.colors[2]
|
|
|
|
rect = Rectangle(self.evas, color=color)
|
|
self.rects.append(rect)
|
|
self.pack(rect, x, y, 1, 1)
|
|
rect.show()
|
|
p += 1
|
|
|
|
def update(self, pieces, num_pieces):
|
|
self.pieces = pieces
|
|
if pieces and not self.rects:
|
|
self.pack_rects()
|
|
elif self.num_completed_pieces == num_pieces:
|
|
return
|
|
self.num_completed_pieces = num_pieces
|
|
width, height = self.size
|
|
old_blocks = self.blocks
|
|
self._blocks = None
|
|
new_blocks = self.blocks
|
|
row = 0
|
|
col = 0
|
|
for ov, nv in zip(old_blocks, new_blocks):
|
|
if ov != nv:
|
|
self.rects[(row * width) + col].color = self.colors[nv]
|
|
|
|
if (col + 1) >= width:
|
|
row += 1
|
|
col = 0
|
|
else:
|
|
col += 1
|
|
|
|
return True
|
|
|
|
|
|
class ActSWithLabel(Box):
|
|
|
|
def __init__(self, parent, label_text, values, initial_value):
|
|
Box.__init__(self, parent)
|
|
|
|
from efl.elementary.actionslider import Actionslider, \
|
|
ELM_ACTIONSLIDER_LEFT, ELM_ACTIONSLIDER_CENTER, \
|
|
ELM_ACTIONSLIDER_RIGHT, ELM_ACTIONSLIDER_ALL
|
|
|
|
self.pos_to_v = {
|
|
ELM_ACTIONSLIDER_LEFT: values[0],
|
|
ELM_ACTIONSLIDER_CENTER: values[1],
|
|
ELM_ACTIONSLIDER_RIGHT: values[2],
|
|
}
|
|
self.v_to_pos = {
|
|
values[0]: ELM_ACTIONSLIDER_LEFT,
|
|
values[1]: ELM_ACTIONSLIDER_CENTER,
|
|
values[2]: ELM_ACTIONSLIDER_RIGHT,
|
|
}
|
|
|
|
self.horizontal = True
|
|
self.size_hint_align = FILL_HORIZ
|
|
self.size_hint_weight = EXPAND_HORIZ
|
|
|
|
l = Label(parent)
|
|
l.text = label_text
|
|
l.show()
|
|
w = self.w = Actionslider(parent)
|
|
w.magnet_pos = ELM_ACTIONSLIDER_ALL
|
|
w.size_hint_align = FILL_HORIZ
|
|
w.size_hint_weight = EXPAND_HORIZ
|
|
w.show()
|
|
|
|
parts = "left", "center", "right"
|
|
|
|
for i, v in enumerate(values):
|
|
w.part_text_set(parts[i], str(v))
|
|
|
|
init_v = self.v_to_pos[initial_value]
|
|
w.indicator_pos = init_v
|
|
|
|
self.pack_end(l)
|
|
self.pack_end(w)
|
|
|
|
def get_value(self):
|
|
pos = self.w.indicator_pos
|
|
value = self.pos_to_v[pos]
|
|
return value
|
|
|
|
|
|
class RangeSpinners(Box):
|
|
|
|
def __init__(self, parent, low, high, minim, maxim):
|
|
|
|
Box.__init__(self, parent)
|
|
self.size_hint_weight = EXPAND_BOTH
|
|
self.size_hint_align = FILL_BOTH
|
|
self.horizontal = True
|
|
|
|
l = self.listenlow = Spinner(parent)
|
|
l.size_hint_weight = EXPAND_BOTH
|
|
l.size_hint_align = FILL_BOTH
|
|
l.min_max = minim, maxim
|
|
l.value = low
|
|
self.pack_end(l)
|
|
l.show()
|
|
|
|
h = self.listenhigh = Spinner(parent)
|
|
h.size_hint_weight = EXPAND_BOTH
|
|
h.size_hint_align = FILL_BOTH
|
|
h.min_max = minim, maxim
|
|
h.value = high
|
|
self.pack_end(h)
|
|
h.show()
|
|
|
|
|
|
class FsButton(Fileselector, FileselectorButton):
|
|
|
|
def __init__(self, parent, *args, **kwargs):
|
|
FileselectorButton.__init__(self, parent, *args, **kwargs)
|