# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 Rickard Lindberg, Roger Lindberg
#
# This file is part of Timeline.
#
# Timeline 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.
#
# Timeline 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 Timeline. If not, see <http://www.gnu.org/licenses/>.
import os
import shutil
import wx
from timelinelib.wxgui.framework import Controller
from timelinelib.wxgui.dialogs.slideshow.templates import CSS
from timelinelib.wxgui.dialogs.slideshow.templates import IMAGE_AND_DESCRIPTION
from timelinelib.wxgui.dialogs.slideshow.templates import ONLY_DESCRIPTION
from timelinelib.wxgui.dialogs.slideshow.templates import PAGE_TEMPLATE
from timelinelib.config.paths import ICONS_DIR
DIR_IS_MANDATORY = _("The html pages directory is mandatory")
CANT_FIND_DIR = _("Can't find the html pages directory!") + "\n" + _("Do you want to create it?")
OVERWRITE_DIR = _("The html pages directory isn't empty!") + "\n" + _("Do you want overwrite it?")
[docs]class SlideshowDialogController(Controller):
[docs] def on_init(self, db, canvas):
self._db = db
self._canvas = canvas
self._text_transformer = self._install_text_transformer_plugin()
[docs] def on_change_dir(self, evt):
self.view.ChangeDir()
[docs] def on_start(self, evt):
if self._input_is_valid():
self._create_and_start_slideshow()
self.view.EndModalOk()
def _input_is_valid(self):
if self._target_dir_not_given():
self.view.InvalidTargetDir(DIR_IS_MANDATORY)
return False
if not self._target_dir_exists():
if not self.view.GetUserAck(CANT_FIND_DIR):
return False
os.mkdir(self.view.GetTargetDir())
elif self._target_dir_is_not_empty():
if not self.view.GetUserAck(OVERWRITE_DIR):
return False
return True
def _target_dir_not_given(self):
return len(self.view.GetTargetDir().strip()) == 0
def _target_dir_exists(self):
return os.path.exists(self.view.GetTargetDir())
def _target_dir_is_not_empty(self):
return len(os.listdir(self.view.GetTargetDir())) > 0
def _create_and_start_slideshow(self):
events = self._get_events()
if len(events) > 0:
self._create_images(events)
self._create_css()
self._create_pages(events)
self.view.DisplayStartPage(os.path.join(self.view.GetTargetDir(), "page_1.html"))
def _get_events(self):
if self.view.AllEventsSelected():
func = self._get_all_events
else:
func = self._get_visible_events
return sorted([event for event in func() if not event.is_container()], key=lambda event: event.get_start_time())
def _get_all_events(self):
return self._db.get_all_events()
def _get_visible_events(self):
vp = self._canvas.GetViewProperties()
return vp.filter_events(self._db.get_events(vp.displayed_period))
def _create_images(self, events):
shutil.copy(os.path.join(ICONS_DIR, "32.png"), os.path.join(self.view.GetTargetDir(), "32.png"))
self._image_source = [""]
inx = 0
for event in events:
inx += 1
icon = event.get_icon()
if icon:
icon.SaveFile(os.path.join(self.view.GetTargetDir(), "icon_img_%d.bmp" % inx), wx.BITMAP_TYPE_BMP)
self._image_source.append("icon_img_%d.bmp" % inx)
else:
self._image_source.append("")
def _create_css(self):
with open(os.path.join(self.view.GetTargetDir(), "slideshow.css"), "w") as f:
f.write(CSS.encode('utf8', 'ignore').decode('utf-8'))
def _create_pages(self, events):
nbr_of_pages = len(events)
page_nbr = 0
w1 = events[-1].get_start_time() - events[0].get_start_time()
pos_style = self._get_positions_style(events)
pos_history = self._get_position_history(len(events))
for event in events:
w2 = event.get_start_time() - events[0].get_start_time()
p = self._calc_history_pos(w1, w2, events, event)
page_nbr += 1
next_page_nbr = self._get_next_page_nbr(page_nbr, nbr_of_pages)
prev_page_nbr = self._get_prev_page_nbr(page_nbr, nbr_of_pages)
self._create_page(pos_style, p, event, page_nbr, next_page_nbr, prev_page_nbr, pos_history)
def _calc_history_pos(self, w1, w2, events, event):
w2 = event.get_start_time() - events[0].get_start_time()
try:
return 2 + int(90 * (w2 / w1))
except ZeroDivisionError:
return 2
def _get_next_page_nbr(self, page_nbr, nbr_of_pages):
if page_nbr == nbr_of_pages:
return 1
else:
return page_nbr + 1
def _get_prev_page_nbr(self, page_nbr, nbr_of_pages):
if page_nbr == 1:
return nbr_of_pages
else:
return page_nbr - 1
def _create_page(self, pos_style, p, event, page_nbr, next_page_nbr, prev_page_nbr, pos_history):
with open(os.path.join(self.view.GetTargetDir(), "page_%d.html" % page_nbr), "w") as f:
if self._image_source[page_nbr] == "":
x = ONLY_DESCRIPTION % self._text_transformer.transform(event.get_description())
else:
x = IMAGE_AND_DESCRIPTION % (self._image_source[page_nbr],
self._text_transformer.transform(event.get_description()))
pg = PAGE_TEMPLATE % (pos_style,
p,
self._db.get_time_type().format_period(event.get_time_period()),
event.get_text(),
x,
prev_page_nbr,
next_page_nbr,
pos_history)
f.write(pg.encode('utf8', 'ignore').decode('utf-8'))
def _install_text_transformer_plugin(self):
from timelinelib.plugin import factory
from timelinelib.plugin.factory import TEXT_TRANSFORMER
from timelinelib.plugin.plugins.texttransformers.defaulttexttransformer import DefaultTextTransformer
plugin = factory.get_plugin(TEXT_TRANSFORMER, "Plain text to HTML transformer") or DefaultTextTransformer()
return plugin.run()
def _get_positions_style(self, events):
template = """div.position_in_history_%d {
border-radius: 50%%;
width: 10px;
height: 10px;
background-color: #66CDAA;
position: fixed;
top: 10px;
left: %d%%
}
"""
collector = []
w1 = events[-1].get_start_time() - events[0].get_start_time()
nbr = 0
for event in events:
nbr += 1
w2 = event.get_start_time() - events[0].get_start_time()
p = self._calc_history_pos(w1, w2, events, event)
collector.append(template % (nbr, p))
return "\n".join(collector)
def _get_position_history(self, count):
template = '<div class="position_in_history_%d"/>'
collector = []
for i in range(count):
collector.append(template % (i + 1))
return "\n".join(collector)