diff --git a/data/css/lightbox.css b/data/css/lightbox.css new file mode 100644 index 000000000..ccaa457be --- /dev/null +++ b/data/css/lightbox.css @@ -0,0 +1,158 @@ +/* +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright 2023- Serge Noiraud +# +# 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 2 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. +# + +************************************************************************************************** +GRAMPS cascading style sheet for common lightbox +Style Name: n/a (used by many different styles) +Style Author: Serge Noiraud based on W3C: https://www.w3schools.com/howto/howto_js_lightbox.asp +**************************************************************************************************/ + +.MediaRow { + z-index: 999; +} + +.MediaRow > .MediaColumn { + padding: 0 8px; +} + +.MediaRow:after { + content: ""; + display: table; + clear: both; +} + +.MediaColumn { + float: left; + width: 15%; + height: 100px; +} + +/* The Modal (background) */ +.ModalClass { + display: none; + position: fixed; + z-index: 900; + padding-top: 5px; + left: 0px; + top: 0px; + width: 100%; + height: 100%; + overflow: auto; +} + +/* Modal Content */ +.ModalContent { + position: relative; + background-color: #fefefe; + margin: auto; + padding: 0; + width: 95%; +} + +/* The Close Button */ +.MediaClose { + color: black; + position: absolute; + z-index: 960; + top: 10px; + right: 1%; + font-size: 48px; + font-weight: bold; +} + +.MediaClose:hover, +.MediaClose:focus { + color: #999; + text-decoration: none; + cursor: pointer; +} + +.MediaSlide { + display: none; +} + +.MediaCursor { + cursor: pointer; +} + +/* Next & previous buttons */ +.MediaPrev, +.MediaNext { + cursor: pointer; + position: fixed; + width: auto; + top: 300px; + padding: 16px; + margin-top: -50px; + color: black; + font-weight: bold; + font-size: 48px; + transition: 0.6s ease; + border-radius: 0 3px 3px 0; + user-select: none; + -webkit-user-select: none; + background-color: rgba(255, 255, 255, 0.3); +} + +/* Position the "next button" to the right */ +.MediaNext { + right: 2%; + border-radius: 3px 0 0 3px; +} + +/* On hover, add a black background color with a little bit see-through */ +.MediaPrev:hover, +.MediaNext:hover { + background-color: rgba(0, 0, 0, 0.1); +} + +/* Number text (1/3 etc) */ +.MediaNumber { + font-size: 36px; + font-weight: bold; + padding: 8px 12px; + position: absolute; + color: black; + background-color: rgba(255, 255, 255, 0.4); +} + +/* Less whitespace on smaller real estate. */ +@media only screen and (max-width: 1080px) { + .MediaNumber { + font-size: 12px; + } + .MediaPrev, + .MediaNext { + top: 100px; + font-size: 24px; + } + .MediaClose { + font-size: 36px; + } +} + +img.hover-shadow { + transition: 0.3s; +} + +.hover-shadow:hover { + box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); +} diff --git a/data/css/lightbox.js b/data/css/lightbox.js new file mode 100644 index 000000000..7f3b56fed --- /dev/null +++ b/data/css/lightbox.js @@ -0,0 +1,60 @@ +/* +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright 2023- Serge Noiraud +# +# 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 2 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. +# + +************************************************************************************************** +GRAMPS javascript for lightbox feature +Style Name: n/a (used by many different styles) +Javascript Author: Serge Noiraud based on W3C: https://www.w3schools.com/howto/howto_js_lightbox.asp +**************************************************************************************************/ + +// Open the Modal +function openModal() { + document.getElementById("MediaModal").style.display = "block"; +} + +// Close the Modal +function closeModal() { + document.getElementById("MediaModal").style.display = "none"; +} + +var slideIndex = 1; +showSlides(slideIndex); + +// Next/previous controls +function plusSlides(n) { + showSlides(slideIndex += n); +} + +// Thumbnail image controls +function currentSlide(n) { + showSlides(slideIndex = n); +} + +function showSlides(n) { + var i; + var slides = document.getElementsByClassName("MediaSlide"); + if (n > slides.length) {slideIndex = 1} + if (n < 1) {slideIndex = slides.length} + for (i = 0; i < slides.length; i++) { + slides[i].style.display = "none"; + } + slides[slideIndex-1].style.display = "block"; +} diff --git a/gramps/plugins/webreport/basepage.py b/gramps/plugins/webreport/basepage.py index 8d7fc5b08..1de942a2b 100644 --- a/gramps/plugins/webreport/basepage.py +++ b/gramps/plugins/webreport/basepage.py @@ -2268,15 +2268,19 @@ class BasePage: # make referenced images have the same order as in media list: photolist_handles = {} + self.max_img = 0 for mediaref in photolist: photolist_handles[mediaref.get_reference_handle()] = mediaref + photo_handle = mediaref.get_reference_handle() + photo = self.r_db.get_media_from_handle(photo_handle) + mime_type = photo.get_mime_type() + if "image" in mime_type: + self.max_img += 1 photolist_ordered = [] for photoref in copy.copy(object_.get_media_list()): if photoref.ref in photolist_handles: photo = photolist_handles[photoref.ref] photolist_ordered.append(photo) - # and add any that are left (should there be any?) - photolist_ordered += photolist # begin individualgallery division and section title with Html("div", class_="subsection", id="indivgallery") as section: @@ -2288,46 +2292,120 @@ class BasePage: with Html("div", style="display:%s" % disp, id="toggle_media") as toggle: section += toggle - displayed = [] - for mediaref in photolist_ordered: + with Html("div", id="medias") as medias: + displayed = [] + # Create the list of media for lightbox + for mediaref in photolist_ordered: + photo_handle = mediaref.get_reference_handle() + photo = self.r_db.get_media_from_handle(photo_handle) + if photo_handle in displayed: + continue + # get media description + descr = photo.get_description() + mime_type = photo.get_mime_type() + if mime_type and "image" not in mime_type: + try: + # create thumbnail url + # extension needs to be added as it is not + # already there + url_thumb = (self.report.build_url_fname( + photo_handle, "thumb", True, + image=True) + ".png") + # begin hyperlink + medias += self.media_link(photo_handle, + url_thumb, descr, + uplink=self.uplink, + usedescr=True) + except (IOError, OSError) as msg: + self.r_user.warn(_("Could not add photo to page"), + str(msg)) + elif not mime_type: + try: + # begin hyperlink + medias += self.doc_link(photo_handle, descr, + uplink=self.uplink) + except (IOError, OSError) as msg: + self.r_user.warn(_("Could not add photo to page"), + str(msg)) + # First part for lightbox + lightbox = 0 + with Html("div", class_="MediaRow") as lightboxes_1: + for mediaref in photolist_ordered: + photo_handle = mediaref.get_reference_handle() + photo = self.r_db.get_media_from_handle(photo_handle) + mime_type = photo.get_mime_type() + if mime_type and "image" in mime_type: + with Html("div", class_="MediaColumn") as boxes: + lightboxes_1 += boxes + try: + url_thumb = (self.report.build_url_fname( + photo_handle, "thumb", + True, image=True) + ".png") + # begin hyperlink + lightbox += 1 + boxes += Html("img", src=url_thumb, + inline=True, + onclick="openModal();currentSlide(%d)" % lightbox, + class_="hover-shadow") + except (IOError, OSError) as msg: + self.r_user.warn(_("Could not add photo to page"), + str(msg)) + # Second part for lightbox + lightbox = 0 + with Html("div", id="MediaModal", + class_="ModalClass") as lightboxes_2: + lightboxes_2 += Html("span", "×", + onclick="closeModal()", + class_="MediaClose MediaCursor", + inline=True) + with Html("div", class_="ModalContent") as lb_content: + lightboxes_2 += lb_content + lb_content += Html("a", "❮", + class_="MediaPrev", + onclick="plusSlides(-1)") + lb_content += Html("a", "❯", + class_="MediaNext", + onclick="plusSlides(1)") + for mediaref in photolist_ordered: + photo_handle = mediaref.get_reference_handle() + photo = self.r_db.get_media_from_handle(photo_handle) + mime_type = photo.get_mime_type() + if mime_type and "image" in mime_type: + with Html("div", class_="MediaSlide") as box: + lb_content += box + lightbox += 1 + image_number = "%d/%d - %s" % (lightbox, + self.max_img, photo.get_description()) + box += Html("div", image_number, + class_="MediaNumber") + try: + thb_img = ( + self.report.build_url_fname( + photo_handle, "img", True, + image=True) + self.ext) + fname_, ext = os.path.splitext(photo.get_path()) + url_img = (self.report.build_url_fname(photo_handle, + "images", + True, + image=True) + ext) - photo_handle = mediaref.get_reference_handle() - photo = self.r_db.get_media_from_handle(photo_handle) - - if photo_handle in displayed: - continue - mime_type = photo.get_mime_type() - - # get media description - descr = photo.get_description() - - if mime_type: - try: - # create thumbnail url - # extension needs to be added as it is not - # already there - url = (self.report.build_url_fname(photo_handle, - "thumb", - True, - image=True) + - ".png") - # begin hyperlink - toggle += self.media_link(photo_handle, url, - descr, - uplink=self.uplink, - usedescr=True) - except (IOError, OSError) as msg: - self.r_user.warn(_("Could not add photo to page"), - str(msg)) - else: - try: - # begin hyperlink - toggle += self.doc_link(photo_handle, descr, - uplink=self.uplink) - except (IOError, OSError) as msg: - self.r_user.warn(_("Could not add photo to page"), - str(msg)) + box += Html("a", href=thb_img) + ( + Html("img", src=url_img, + style="width:100%") + ) + except (IOError, OSError) as msg: + self.r_user.warn(_("Could not add photo to page"), + str(msg)) displayed.append(photo_handle) + if lightboxes_1: + toggle += lightboxes_1 + toggle += lightboxes_2 + if lightbox < len(photolist): + toggle += Html("h3", + self._("Other media: vidéos, pdfs..."), + inline=True) + if medias: + toggle += medias # add fullclear for proper styling section += FULLCLEAR diff --git a/gramps/plugins/webreport/event.py b/gramps/plugins/webreport/event.py index 31c7b8620..aa9d16c0e 100644 --- a/gramps/plugins/webreport/event.py +++ b/gramps/plugins/webreport/event.py @@ -395,7 +395,6 @@ class EventPages(BasePage): ldatec = event.get_change_time() event_media_list = event.get_media_list() - self.uplink = True subdirs = True evt_type = self._(event.get_type().xml_str()) @@ -404,7 +403,19 @@ class EventPages(BasePage): output_file, sio = self.report.create_file(event_handle, "evt") result = self.write_header(self._("Events")) - eventpage, dummy_head, dummy_body, outerwrapper = result + eventpage, head, dummy_body, outerwrapper = result + if event_media_list and self.create_media: + if self.the_lang and not self.usecms: + fname = "/".join(["..", "css", "lightbox.css"]) + jsname = "/".join(["..", "css", "lightbox.js"]) + else: + fname = "/".join(["css", "lightbox.css"]) + jsname = "/".join(["css", "lightbox.js"]) + url = self.report.build_url_fname(fname, None, self.uplink) + head += Html("link", href=url, type="text/css", + media="screen", rel="stylesheet") + url = self.report.build_url_fname(jsname, None, self.uplink) + head += Html("script", src=url, type="text/javascript", inline=True) # start event detail division with Html("div", class_="content", id="EventDetail") as eventdetail: diff --git a/gramps/plugins/webreport/family.py b/gramps/plugins/webreport/family.py index 449ed27d7..41677b1d4 100644 --- a/gramps/plugins/webreport/family.py +++ b/gramps/plugins/webreport/family.py @@ -390,7 +390,7 @@ class FamilyPages(BasePage): output_file, sio = self.report.create_file(family.get_handle(), "fam") result = self.write_header(family_name) - familydetailpage, dummy_head, dummy_body, outerwrapper = result + familydetailpage, head, dummy_body, outerwrapper = result # begin FamilyDetaill division with Html("div", class_="content", @@ -400,6 +400,18 @@ class FamilyPages(BasePage): # family media list for initial thumbnail if self.create_media: media_list = family.get_media_list() + if media_list: + if self.the_lang and not self.usecms: + fname = "/".join(["..", "css", "lightbox.css"]) + jsname = "/".join(["..", "css", "lightbox.js"]) + else: + fname = "/".join(["css", "lightbox.css"]) + jsname = "/".join(["css", "lightbox.js"]) + url = self.report.build_url_fname(fname, None, self.uplink) + head += Html("link", href=url, type="text/css", + media="screen", rel="stylesheet") + url = self.report.build_url_fname(jsname, None, self.uplink) + head += Html("script", src=url, type="text/javascript", inline=True) # If Event pages are not being created, then we need to display # the family event media here if not self.inc_events: @@ -429,7 +441,6 @@ class FamilyPages(BasePage): relationshipdetail += families # display additional images as gallery - if self.create_media and media_list: addgallery = self.disp_add_img_as_gallery(media_list, family) if addgallery: relationshipdetail += addgallery diff --git a/gramps/plugins/webreport/narrativeweb.py b/gramps/plugins/webreport/narrativeweb.py index 5c65296e7..089d59c3f 100644 --- a/gramps/plugins/webreport/narrativeweb.py +++ b/gramps/plugins/webreport/narrativeweb.py @@ -1150,6 +1150,12 @@ class NavWebReport(Report): fname = CSS["behaviour"]["filename"] self.copy_file(fname, "behaviour.css", "css") + # copy lightbox style sheet and javascript + fname = CSS["lightbox"]["filename"] + self.copy_file(fname, "lightbox.css", "css") + fname = CSS["lightbox_js"]["filename"] + self.copy_file(fname, "lightbox.js", "css") + # copy Menu Layout Style Sheet if Blue or Visually is being # used as the stylesheet? if CSS[self.css]["navigation"]: diff --git a/gramps/plugins/webreport/person.py b/gramps/plugins/webreport/person.py index 7064defb7..737c7f2aa 100644 --- a/gramps/plugins/webreport/person.py +++ b/gramps/plugins/webreport/person.py @@ -510,7 +510,7 @@ class PersonPages(BasePage): output_file, sio = self.report.create_file(person.get_handle(), "ppl") self.uplink = True result = self.write_header(self.sort_name) - indivdetpage, dummy_head, dummy_body, outerwrapper = result + indivdetpage, head, dummy_body, outerwrapper = result # begin individualdetail division with Html("div", class_="content", @@ -587,6 +587,18 @@ class PersonPages(BasePage): media_list += event.get_media_list() # display additional images as gallery + if photo_list and self.create_media: + if self.the_lang and not self.usecms: + fname = "/".join(["..", "css", "lightbox.css"]) + jsname = "/".join(["..", "css", "lightbox.js"]) + else: + fname = "/".join(["css", "lightbox.css"]) + jsname = "/".join(["css", "lightbox.js"]) + url = self.report.build_url_fname(fname, None, self.uplink) + head += Html("link", href=url, type="text/css", + media="screen", rel="stylesheet") + url = self.report.build_url_fname(jsname, None, self.uplink) + head += Html("script", src=url, type="text/javascript", inline=True) sect7 = self.disp_add_img_as_gallery(media_list, person) if sect7 is not None: individualdetail += sect7 diff --git a/gramps/plugins/webreport/place.py b/gramps/plugins/webreport/place.py index a46baf117..ee94cf07b 100644 --- a/gramps/plugins/webreport/place.py +++ b/gramps/plugins/webreport/place.py @@ -375,7 +375,18 @@ class PlacePages(BasePage): outerwrapper += placedetail media_list = place.get_media_list() - if self.create_media: + if media_list and self.create_media: + if self.the_lang and not self.usecms: + fname = "/".join(["..", "css", "lightbox.css"]) + jsname = "/".join(["..", "css", "lightbox.js"]) + else: + fname = "/".join(["css", "lightbox.css"]) + jsname = "/".join(["css", "lightbox.js"]) + url = self.report.build_url_fname(fname, None, self.uplink) + head += Html("link", href=url, type="text/css", + media="screen", rel="stylesheet") + url = self.report.build_url_fname(jsname, None, self.uplink) + head += Html("script", src=url, type="text/javascript", inline=True) thumbnail = self.disp_first_img_as_thumbnail(media_list, place) if thumbnail is not None: diff --git a/gramps/plugins/webreport/source.py b/gramps/plugins/webreport/source.py index 35f9bb598..71bf79575 100644 --- a/gramps/plugins/webreport/source.py +++ b/gramps/plugins/webreport/source.py @@ -233,7 +233,7 @@ class SourcePages(BasePage): self.uplink = True result = self.write_header("%s - %s" % (self._('Sources'), self.page_title)) - sourcepage, dummy_head, dummy_body, outerwrapper = result + sourcepage, head, dummy_body, outerwrapper = result ldatec = 0 # begin source detail division @@ -242,6 +242,17 @@ class SourcePages(BasePage): media_list = source.get_media_list() if self.create_media and media_list: + if self.the_lang and not self.usecms: + fname = "/".join(["..", "css", "lightbox.css"]) + jsname = "/".join(["..", "css", "lightbox.js"]) + else: + fname = "/".join(["css", "lightbox.css"]) + jsname = "/".join(["css", "lightbox.js"]) + url = self.report.build_url_fname(fname, None, self.uplink) + head += Html("link", href=url, type="text/css", + media="screen", rel="stylesheet") + url = self.report.build_url_fname(jsname, None, self.uplink) + head += Html("script", src=url, type="text/javascript", inline=True) thumbnail = self.disp_first_img_as_thumbnail(media_list, source) if thumbnail is not None: diff --git a/gramps/plugins/webstuff/webstuff.py b/gramps/plugins/webstuff/webstuff.py index eaacb5820..fd7555b89 100644 --- a/gramps/plugins/webstuff/webstuff.py +++ b/gramps/plugins/webstuff/webstuff.py @@ -107,6 +107,14 @@ def load_on_reg(dbstate, uistate, plugin): ["behaviour", 0, "Behaviour", path_css('behaviour.css'), None, [], []], + # media lightbox style sheet + ["lightbox", 0, "", + path_css('lightbox.css'), None, [], []], + + # media lightbox javascript + ["lightbox_js", 0, "", + path_css('lightbox.js'), None, [], []], + # NarrativeMap stylesheet/ image for NarrativeWeb place maps ["NarrativeMaps", 0, "", path_css("narrative-maps.css"), None, [], []],