# self.py
#
# Copyright 2020 Fabio
#
# 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, see <http://www.gnu.org/licenses/>.
import datetime
import re
from gi.repository import GLib
from gi.repository import Gtk
from gi.repository import GObject
from gi.repository import GdkPixbuf
from gi.repository import Gio
from gi.repository import Handy
from gi.repository import Notify
from .config import APP_NAME, APP_ID, APP_VERSION
from . import pages
from . import local
from . import models
from .preferences import PreferencesWindow
from .widgets import LoadingWidget, ErrorWidget, PlaceholderWidget
from .widgets import NotificationOverlay, LoadingOverlay, SearchBarOverlay
from .widgets import InputDialog, ConferenceEditDialog
class NetworkConnectionMonitor(GObject.Object):
def __init__(self):
super().__init__()
self._isconnected = False
self.nm = Gio.NetworkMonitor.get_default()
self.nm.connect("network-changed", self.on_network_changed)
self.on_network_changed()
def on_network_changed(self, *args):
_ic = self._isconnected
self._isconnected = self.nm.get_connectivity() == Gio.NetworkConnectivity.FULL
if (self._isconnected != _ic):
#print("NetworkConnectionMonitor.isconnected", self._isconnected)
self.notify("isconnected")
@GObject.Property
def isconnected(self):
"""The system can reach the global netowrk. Bool, read-only"""
#print("NetworkConnectionMonitor.isconnected getter:", self._isconnected)
return self._isconnected
class StartView(pages.BaseListPage):
"""
window
overlay
notification
loading
title_bar
open_button
search_button
update_button
box
search
column
box <VBox>
titlebox <VBox> (if title or subtitle is set)
title <Label>
subtitle <Label>
frame
listbox
"""
def __init__(self, window, **kwargs):
self.window = window
self.nm = NetworkConnectionMonitor()
self.data = []
self.empty_placeholder = PlaceholderWidget(
APP_NAME,
_("No conferences yet.") + "\n" + \
_("Add a conference with the add button in title bar."))
self.empty_placeholder.show_all()
self.nomatch_placeholder = PlaceholderWidget(
text = _("No matches found"),
icon_name = 'system-search'
)
self.nomatch_placeholder.show_all()
super().__init__(**kwargs)
self.get_style_context().add_class("start-view")
self.get_style_context().add_class("clean-list")
self.column.set_maximum_size(500)
title_bar = Handy.HeaderBar(
centering_policy = Handy.CenteringPolicy.STRICT,
show_close_button = True,
hexpand = True,
title = APP_NAME
)
self.listbox.set_placeholder(self.empty_placeholder)
fetcher = local.open_menu(self.nm.props.isconnected)
if fetcher is not None:
self.window.loading.show(fetcher)
fetcher.connect("done", self.update_list)
fetcher.connect("error", lambda s,e: self.window.notification.show(_("<b>Error loading events list:</b>\n{}").format(e)))
else:
self.update_list()
# app menu
appmenumodel = Gio.Menu()
self.window.pop_menu(appmenumodel)
self.start_button = Gtk.Button.new_from_icon_name('open-menu-symbolic', 1)
self.start_button.set_tooltip_text(_("Show window menu"))
appmenu = Gtk.Popover.new_from_model(self.start_button, appmenumodel)
self.start_button.connect('clicked', lambda _: appmenu.popup())
title_bar.pack_end(self.start_button)
self.update_button = Gtk.Button.new_from_icon_name('view-refresh-symbolic', 1)
self.update_button.set_tooltip_text(_("Refresh events list"))
self.update_button.set_sensitive(self.nm.props.isconnected)
self.update_button.connect('clicked', self.update_schedules)
title_bar.pack_end(self.update_button)
self.open_button = Gtk.Button.new_from_icon_name('list-add-symbolic', 1)
self.open_button.set_tooltip_text(_("Add event from URL"))
self.open_button.set_sensitive(self.nm.props.isconnected)
self.open_button.connect('clicked', self.open_custom_url)
title_bar.pack_start(self.open_button)
_search_button_icon = Gtk.Image.new_from_pixbuf(
Gtk.IconTheme.get_default().load_icon(
'system-search-symbolic', 16, 0))
self.search_button = Gtk.ToggleButton.new()
self.search_button.set_image(_search_button_icon)
self.search_button.set_tooltip_text(_("Find event"))
self.search_button.bind_property("active",
self.window.searchbar, "search-mode-enabled",
GObject.BindingFlags.BIDIRECTIONAL | GObject.BindingFlags.SYNC_CREATE )
title_bar.pack_start(self.search_button)
# search event
self.window.searchbar.entry.connect('notify::text', self.on_do_search)
self.window.searchbar.connect('notify::search-mode-enabled', self.on_do_search)
# can't do this because: Warning: ../glib/gobject/gbinding.c:271: Unable to convert a value of type PyObject to a value of type gboolean
#self.nm.bind_property("isconnected", update_action, "enabled", 0 )
self.nm.connect("notify",self.on_networkconnectionmonitor_notify)
window.set_titlebar(title_bar)
title_bar.show_all()
self.show_all()
def on_do_search(self, *args):
self.update_list()
def on_networkconnectionmonitor_notify(self, *args):
is_connected = self.nm.props.isconnected
self.update_button.set_sensitive(is_connected)
self.open_button.set_sensitive(is_connected)
def update_schedules(self, *args):
fetcher = local.update_menu()
def _done(s, f):
self.update_list()
self.window.notification.show(_("<b>Events list updated</b>"))
def _error(s, e):
self.window.notification.show(_("<b>Error loading events list:</b>\n{}").format(e))
self.window.loading.show(fetcher)
fetcher.connect("done", _done)
fetcher.connect("error", _error)
def edit_meta(self, url):
conf = models.Conference.by_url(url)
if conf is None:
return
d = ConferenceEditDialog(self.window, conf)
ret = d.run()
if ret == ConferenceEditDialog.OK:
conf = d.get_conf()
local.update_user_menu(conf.to_json())
with conf.get_meta() as m:
m.title = conf.title
m.start = conf.start
m.end = conf.end
self.update_list()
d.destroy()
return conf
def open_custom_url(self, *args):
def _cbk(meta):
conf = self.edit_meta(meta.url)
self.update_list()
self.window.notification.show(_("<b>New event:</b>\n{}").format(conf.title))
def _error(s, e):
self.window.notification.show(_("<b>Error:</b>\n{}").format(e))
dialog = InputDialog(self.window)
res = dialog.run()
if res == InputDialog.OK:
url = dialog.get_text()
try:
fetcher = local.add_user_menu(url, _cbk)
if fetcher is not None:
fetcher.connect("error", _error)
except local.MenuItemAlreadyExistsException as e:
# show existing obj
conf = models.Conference()._init(e.obj)
self.window.show_main(conf)
except Exception as e:
import traceback; traceback.print_exc()
self.window.notification.show(_("<b>Error:</b>\n{}").format(e))
else:
if fetcher is not None:
self.window.loading.show(fetcher)
dialog.destroy()
def get_objects(self):
confs = self.data
sb = self.window.searchbar
try:
confs = models.Conference.all()
# if searchbar is visible and search text is not "", filter confs by title and url
if (sb.get_search_mode() and sb.entry.get_text() != ""):
self.listbox.set_placeholder(self.nomatch_placeholder)
search = sb.entry.get_text().lower()
confs = [ conf for conf in confs
if search in conf.title.lower() or search in conf.url.lower() ]
else:
self.listbox.set_placeholder(self.empty_placeholder)
except Exception as e:
self.window.notification.show(_("<b>Error loading events list:</b>\n{}").format(e))
return confs
def group_by(self, obj):
if obj.end < datetime.date.today():
return _("Past")
else:
return _("Coming up")
def build_row(self, obj):
row = Handy.ActionRow(activatable=True, selectable=False)
row.set_title(obj.title)
return row
def on_activate(self, listbox, actionrow):
idx = actionrow.get_index()
obj = self.data[idx]
self.window.show_main(obj)
class MainView(Gtk.VBox):
"""
window
overlay
notification
loading
title_bar
back_button
search_button
'squeezer'
top_switcher
title_label
update_button
start_button
box
search
box (self)
mainstack
leaflet ("schedule")
main_menu
Gtk.Separator
stack_schedule <Handy.Deck>
[pages]
stack_starred ("starred") <Handy.Deck>
starred
[pages]
map ("map")
bottom_switcher
TODO: this view is handling too much things!
"""
def __init__(self, window, conf):
super().__init__()
title_bar = Handy.HeaderBar(
centering_policy = Handy.CenteringPolicy.STRICT,
show_close_button = True,
hexpand = True
)
window.set_titlebar(title_bar)
self.window = window
self.conf = conf
self.notifications = {} # keep track of desktop notifications
self.nm = NetworkConnectionMonitor()
# app menu, it's always usefull
appmenumodel = Gio.Menu()
if conf.user:
appmenumodel.append(_("Edit info"), "win.edit-meta")
appmenumodel.append(_("Return to events list"), "win.return-to-events")
self.window.pop_menu(appmenumodel)
self.start_button = Gtk.Button.new_from_icon_name('open-menu-symbolic', 1)
self.start_button.set_tooltip_text(_("Show window menu"))
appmenu = Gtk.Popover.new_from_model(self.start_button, appmenumodel)
self.start_button.connect('clicked', lambda _: appmenu.popup())
title_bar.pack_end(self.start_button)
self.url = conf.url
# open db update should be triggered by network state and data age
try:
fetcher = local.openconf(conf, is_online=self.nm.props.isconnected)
except Exception as e:
e = ErrorWidget(_("Error loading event"), e)
self.pack_start(e, True, True, 0)
title_bar.show_all()
self.show_all()
else:
if fetcher is not None:
fetcher.connect("done", self.on_fetcher_done)
fetcher.connect("error", self.on_fetcher_error)
self.pack_start(LoadingWidget(fetcher), True, True, 0)
title_bar.show_all()
self.show_all()
else:
self.build_gui()
def build_gui(self):
title_bar = self.window.get_titlebar()
# title bar buttons:
self.update_button = Gtk.Button.new_from_icon_name('view-refresh-symbolic', 1)
self.update_button.set_tooltip_text("Refresh event")
self.update_button.set_sensitive(self.nm.props.isconnected)
self.update_button.connect('clicked', self.update_event)
title_bar.pack_end(self.update_button)
self.nm.connect("notify",self.on_networkconnectionmonitor_notify)
# back button
self.back_button = Gtk.Button.new_from_icon_name(
'go-previous-symbolic', 1)
self.back_button.set_tooltip_text(_("Go back"))
self.back_button.connect('clicked', self.goBack)
title_bar.pack_start(self.back_button)
# search button
_search_button_icon = Gtk.Image.new_from_pixbuf(
Gtk.IconTheme.get_default().load_icon(
'system-search-symbolic', 16, 0))
self.search_button = Gtk.ToggleButton.new()
self.search_button.set_image(_search_button_icon)
self.search_button.set_tooltip_text(_("Find talk"))
self.search_button.bind_property("active",
self.window.searchbar, "search-mode-enabled",
GObject.BindingFlags.BIDIRECTIONAL | GObject.BindingFlags.SYNC_CREATE )
title_bar.pack_start(self.search_button)
# search event
self.window.searchbar.entry.connect('notify::text', self.on_do_search)
self.window.searchbar.connect('notify::search-mode-enabled', self.on_search_mode_changed)
# mainstack
self.mainstack = Gtk.Stack(
transition_type=Gtk.StackTransitionType.NONE,
vexpand_set=True,
vexpand=True)
self.leaflet = Handy.Leaflet(
can_swipe_back=True,
hhomogeneous_folded=True,
hhomogeneous_unfolded=False)
self.leaflet.connect('notify::folded', self.on_leaflet_notify)
self.leaflet.connect('notify::visible-child', self.on_leaflet_notify)
self.main_menu = pages.MainMenuPage(pageStack=self, conf=self.conf)
self.stack_schedule = Handy.Deck(
can_swipe_back=True,
can_swipe_forward=False)
self.stack_schedule.connect("notify::transition-running", self.on_stack_changed)
# add main menu and stack to leaflet
self.leaflet.add(self.main_menu)
sep = Gtk.Separator()
self.leaflet.add(sep)
self.leaflet.child_set_property(sep, 'navigatable', False)
self.leaflet.add(self.stack_schedule)
# add first page to stack_schedule
self.stack_schedule.add(pages.ConferencePage(self.conf))
self.stack_schedule.count = 1
# starred
self.stack_starred = Handy.Deck(
can_swipe_back=True,
can_swipe_forward=False)
self.stack_starred.connect("notify::transition-running", self.on_stack_changed)
self.starred = pages.StarredPage(searchbar=self.window.searchbar)
self.starred._set_pagestack(self)
# add first page to stack_starred
self.stack_starred.add(self.starred)
self.stack_starred.count = 1
# set current stack
self.stack = self.stack_schedule
# main stack content
self.mainstack.add_titled(self.leaflet, "schedule", _("Schedule"))
self.mainstack.child_set_property(self.leaflet, "icon-name", "x-office-calendar-symbolic")
self.mainstack.add_titled(self.stack_starred, "starred", _("Starred"))
self.mainstack.child_set_property(self.stack_starred, "icon-name", "starred-symbolic")
if len(self.conf.get_map_links()) > 0:
self.map = pages.MapPage(self.conf)
self.mainstack.add_titled(self.map, "map", _("Map"))
self.mainstack.child_set_property(self.map, "icon-name", "mark-location-symbolic")
# main stack switchers
self.top_switcher = Handy.ViewSwitcher(
policy=Handy.ViewSwitcherPolicy.WIDE,
stack=self.mainstack)
self.bottom_switcher = Handy.ViewSwitcherBar(
stack=self.mainstack)
#mainstack event
self.mainstack.connect("notify::visible-child", self.on_mainstack_changed)
squeezer = Handy.Squeezer()
squeezer.add(self.top_switcher)
title_label = Gtk.Label()
#title_label.set_markup("<b>{}</b>".format(APP_NAME))
squeezer.add(title_label)
title_bar.set_custom_title(squeezer)
squeezer.connect("notify::visible-child",self.on_headerbar_squeezer_notify)
# main box contanier
self.pack_start(self.mainstack, True, True, 0)
self.pack_start(self.bottom_switcher, False, False, 0)
title_bar.show_all()
self.show_all()
# Check if there are notifications to send
Gio.Application.get_default().connect(
'tick', self.check_event_notification
)
self.check_event_notification()
self.update_titlebar_buttons()
# set start mainstack page
self.mainstack.set_visible_child(self.leaflet)
def on_networkconnectionmonitor_notify(self, *args):
is_connected = self.nm.props.isconnected
self.update_button.set_sensitive(is_connected)
def on_mainstack_changed(self, *args):
if (self.mainstack.get_visible_child() == self.stack_starred):
self.stack = self.stack_starred
# get back to starred list when switching main stack
# would be better not, but as we can't update EventDetailPage
# without rebuild it, if use is in a starred EventDetailPage
# then get back to schedule and change something related to that
# event, EventDetailPage objects in stack_starred will be out
# of sync (eg, a new starred event which overlaps)
self.starred.update_list()
self.stack.set_visible_child(self.starred)
self.stack.count = 1
for w in self.stack.get_children()[1:]:
w.destroy()
elif (self.mainstack.get_visible_child() == self.leaflet):
self.stack = self.stack_schedule
self.update_titlebar_buttons()
def on_headerbar_squeezer_notify(self, squeezer, event):
child = squeezer.get_visible_child()
self.bottom_switcher.set_reveal(child != self.top_switcher)
def on_leaflet_notify(self, sender, prop):
self.update_titlebar_buttons()
def on_fetcher_done(self, *args):
self.foreach(lambda obj: obj.destroy())
self.build_gui()
def on_fetcher_error(self, s, e):
self.foreach(lambda obj: obj.destroy())
e = ErrorWidget(_("Error loading event"), e)
self.pack_start(e, True, True, 0)
e.show_all()
def on_do_search(self, *args):
if self.mainstack.get_visible_child_name() == "schedule":
searchpage = None
if self.stack.count > 0:
searchpage = self.stack.get_children()[0]
if searchpage.__class__ != pages.SearchEventsPage:
searchpage = pages.SearchEventsPage(searchbar = self.window.searchbar)
self.replaceWithPage(searchpage)
if self.stack.count > 1:
self.stack.set_visible_child(searchpage)
self.stack.count = 1
for w in self.stack.get_children()[1:]:
w.destroy()
searchpage.update_list()
elif self.mainstack.get_visible_child_name() == "starred":
if self.stack.count > 1:
# reset stack to first page
self.stack.set_visible_child(self.starred)
self.stack.count = 1
for w in self.stack.get_children()[1:]:
w.destroy()
self.starred.update_list()
def on_search_mode_changed(self, *args):
if self.mainstack.get_visible_child_name() == "schedule":
searchmode = self.window.searchbar.get_search_mode()
currentfirstpage = self.stack.get_children()[0]
if not searchmode and currentfirstpage.__class__ == pages.SearchEventsPage:
# search closed, reset to ConferencePage
self.main_menu.select_conference_page()
def edit_meta(self):
d = ConferenceEditDialog(
self.window, self.conf, can_cancel=True, can_delete=True)
ret = d.run()
self.conf = d.get_conf()
d.destroy()
if ret == ConferenceEditDialog.OK:
local.update_user_menu(self.conf.to_json())
with self.conf.get_meta() as m:
m.title = self.conf.title
m.start = self.conf.start
m.end = self.conf.end
self._update_ui()
self.window.notification.show(_("<b>Updated event:</b>\n{}").format(self.conf.title))
if ret == ConferenceEditDialog.DELETE:
local.delete_user_menu(self.conf.to_json())
self.window.notification.show(_("<b>Deleted event:</b>\n{}").format(self.conf.title))
# we could delete cache db here...
# get back to conferences list
self.window.show_start()
def _update_ui(self):
"""update ui when database change. Reset view to ConferencePage"""
self.main_menu.update()
self.mainstack.set_visible_child_name("schedule")
self.main_menu.select_conference_page()
def update_event(self, *args):
"""update cached event data"""
def _done(*args):
self.window.loading.close()
self.window.notification.show(_("<b>Event schedule updated</b>"))
self._update_ui()
def _error(s, e):
self.window.loading.close()
self.window.notification.show(_("<b>Error:</b>\n{}").format(e))
fetcher = local.updatedb(self.url)
self.window.loading.show(fetcher)
fetcher.connect("done", _done)
fetcher.connect("error", _error)
#TODO UPDATE GUI!!! ! !
def check_event_notification(self, *args):
nextup = list(models.Event.nextup(5))
for e in nextup:
# fast and stupid clean html
cleanr = re.compile('<.*?>|&([a-z0-9]+|#[0-9]{1,6}|#x[0-9a-f]{1,6});')
title = re.sub(cleanr, '', e.title)
print("nextup:", e.start_in_tz().strftime("%H:%M"), title, "@", e.room)
self._send_desktop_notification(
_("Next up: {}").format(title),
_("at {} in {}").format(
e.start_in_tz().strftime("%H:%M"),
e.room
),
f"nextup-{e.id}")
e.set_notified(True)
def _send_desktop_notification(self, title, body, nid=None):
if nid in self.notifications:
return
n = Notify.Notification.new(title, body, "net.kirgroup.confy")
n.set_timeout(Notify.EXPIRES_NEVER)
#n.add_action("view", "View", on_notification_action, nid)
n.show()
self.notifications[nid] = n
"""
def _gio_send_desktop_notification(self, title, body, nid=None):
n = Gio.Notification.new(title)
n.set_body(body)
n.set_icon(Gio.ThemedIcon.new("net.kirgroup.confy"))
n.set_priority(Gio.NotificationPriority.HIGH)
print(Gio.Application.get_default(), n)
self.notifications[nid] = n
Gio.Application.get_default().send_notification(nid, n)
"""
def update_titlebar_buttons(self):
search_show = True
back_show = False
if self.mainstack.get_visible_child_name() == "schedule":
folded = self.leaflet.get_property('folded')
firstfold = self.leaflet.get_visible_child() == self.main_menu
back_show = (folded and not firstfold) or self.stack.count > 1
elif self.mainstack.get_visible_child_name() == "starred":
back_show = self.stack.count > 1
self.back_button.set_visible(back_show)
self.search_button.set_visible(search_show)
def on_stack_changed(self, *args):
if self.stack.get_transition_running():
# wait for transition end
return
stack_children = self.stack.get_children()
lastid = len(stack_children) - 1
currentid = stack_children.index(self.stack.get_visible_child())
if currentid == (lastid-1):
# we got back one page, destroy last page
stack_children[lastid].destroy()
self.stack.count = self.stack.count - 1
self.update_titlebar_buttons()
def goBack(self, *args):
if (self.stack.count > 1):
self.stack.navigate(Handy.NavigationDirection.BACK)
"""
#self.stack.set_transition_type(Gtk.StackTransitionType.SLIDE_RIGHT)
current = self.stack.get_children()[-1]
previous = self.stack.get_children()[-2]
self.stack.set_visible_child(previous)
self.stack.count = self.stack.count - 1
folded = self.leaflet.get_property('folded')
while not folded and self.leaflet.get_child_transition_running():
pass
current.destroy()
"""
else:
self.leaflet.set_visible_child(self.main_menu)
self.update_titlebar_buttons()
def pushPage(self, page):
#self.mainstack.set_visible_child(self.leaflet)
page._set_pagestack(self)
self.stack.add(page)
self.stack.count = self.stack.count + 1
page.show_all()
self.leaflet.set_visible_child(self.stack)
self.stack.set_visible_child(page)
self.update_titlebar_buttons()
def replaceWithPage(self, page):
#self.mainstack.set_visible_child(self.leaflet)
page._set_pagestack(self)
self.stack.add(page)
page.show_all()
self.leaflet.set_visible_child(self.stack)
self.stack.set_visible_child(page)
self.stack.count = 1
#folded = self.leaflet.get_property('folded')
#while not folded and self.leaflet.get_child_transition_running():
# pass
for w in self.stack.get_children()[:-1]:
w.destroy()
self.update_titlebar_buttons()
class ConfyWindow(Gtk.ApplicationWindow):
"""
ConfyWindow
overlay
notification <NotificationOverlay>
loading <LoadingOverlay>
box <VBox>
searchbar <SearchBarOverlay>
view <MainView>|<StartView>
"""
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.set_icon_name(APP_ID)
self.set_default_size(800,600)
self.overlay = Gtk.Overlay.new()
self.add(self.overlay)
self.notification = NotificationOverlay()
self.overlay.add_overlay(self.notification)
self.loading = LoadingOverlay()
self.overlay.add_overlay(self.loading)
self.box = Gtk.VBox()
self.searchbar = SearchBarOverlay()
self.box.pack_start(self.searchbar, False, False, 0)
self.overlay.add(self.box)
# win actions
edit_meta_action = Gio.SimpleAction.new("edit-meta", None)
edit_meta_action.connect("activate", self.edit_meta)
self.add_action(edit_meta_action)
return_to_events_action = Gio.SimpleAction.new("return-to-events", None)
return_to_events_action.connect("activate", lambda *_: self.show_start() )
self.add_action(return_to_events_action)
show_preferences_action = Gio.SimpleAction.new("show-preferences", None)
show_preferences_action.connect("activate", self.show_preferences )
self.add_action(show_preferences_action)
search_action = Gio.SimpleAction.new("search", None)
search_action.connect("activate", lambda *_: self.searchbar.set_search_mode(True) )
self.add_action(search_action)
about_action = Gio.SimpleAction.new("about", None)
about_action.connect("activate", self.show_about)
self.add_action(about_action)
self.view = None
self.show_all()
self.show_start()
def show_preferences(self, *args):
pref = PreferencesWindow(transient_for=self)
pref.show_all()
def edit_meta(self, *args):
if isinstance(self.view, MainView):
self.view.edit_meta()
def pop_menu(self, menu):
s = Gio.Menu()
s.append(_("Preferences"), "win.show-preferences")
s.append(_("Keyboard shortcuts"), "app.shortcuts")
s.append(_("About {}").format(APP_NAME), "win.about")
menu.append_section(None, s)
def show_about(self, *args):
Gtk.AboutDialog(
program_name=APP_NAME,
logo_icon_name=APP_ID,
version=APP_VERSION,
copyright="© 2020 Fabio Comuni",
website="https://confy.kirgroup.net/",
authors=["Fabio Comuni", "Evangelos Ribeiro Tzaras", "Sebastian Crane"],
comments=_("Conference schedules viewer"),
license_type=Gtk.License.GPL_3_0
).show()
def show_start(self):
"""start page, list conferences"""
if self.view is not None:
self.view.destroy()
self.searchbar.set_search_mode(False)
self.view = StartView(self)
self.box.pack_start(self.view, True, True, 0)
local.close()
def show_main(self, conf):
"""start page, list conferences"""
if self.view is not None:
self.view.destroy()
self.searchbar.set_search_mode(False)
self.view = MainView(self, conf)
self.box.pack_start(self.view, True, True, 0)