# main.py # # Copyright 2020 Morgan McMillian # # 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 . import sys import gi import os import pnutpy import logging gi.require_version('Gdk', '3.0') gi.require_version('Gtk', '3.0') from gi.repository import GObject, Gdk, Gtk, Gio, GLib gi.require_version('Handy', '1') from gi.repository import Handy Handy.init() from .widgets import LoginPage, Timeline, PostItem, ComposeWindow class Application(Gtk.Application): def __init__(self): super().__init__(application_id='dev.thrrgilag.squeak', flags=Gio.ApplicationFlags.FLAGS_NONE) def do_startup(self): Gtk.Application.do_startup(self) self.authorized = False self.keyfile = GLib.KeyFile() self.config_dir = GLib.get_user_config_dir() self.config_file = os.path.join(self.config_dir, "squeak") try: self.keyfile.load_from_file(self.config_file, GLib.KeyFileFlags.KEEP_COMMENTS) access_token = self.keyfile.get_string("GENERAL", "ACCESS_TOKEN") if len(access_token) > 0: pnutpy.api.add_authorization_token(access_token) self.authorized = True except GLib.Error: pass action = Gio.SimpleAction.new('newpost', None) action.connect('activate', self.on_new_post) self.add_action(action) self.set_accels_for_action('app.newpost',["n"]) action = Gio.SimpleAction.new('top', None) action.connect('activate', self.on_top) self.add_action(action) action = Gio.SimpleAction.new('refresh', None) action.connect('activate', self.on_refresh) self.add_action(action) self.set_accels_for_action('app.refresh',["r"]) action = Gio.SimpleAction.new('preferences', None) action.connect('activate', self.on_preferences) self.add_action(action) # TODO: this shortcut might need adjustment self.set_accels_for_action('app.preferences',[","]) action = Gio.SimpleAction.new('about', None) action.connect('activate', self.on_about) self.add_action(action) action = Gio.SimpleAction.new('quit', None) action.connect('activate', self.on_quit) self.add_action(action) self.set_accels_for_action('app.quit',["q"]) self.builder = Gtk.Builder.new_from_resource("/dev/thrrgilag/squeak/menu.ui") def do_activate(self): self.win = self.props.active_window if not self.win: self.win = Gtk.ApplicationWindow(application=self) self.win.set_default_size(320, 480) title_bar = Handy.TitleBar() self.win.set_titlebar(title_bar) self.header = Handy.HeaderBar(show_close_button=True) title_bar.add(self.header) self.stack = Gtk.Stack() stack_switcher_bar = Handy.ViewSwitcherBar() stack_switcher_bar.set_stack(self.stack) switcher_title = Handy.ViewSwitcherTitle() switcher_title.set_stack(self.stack) self.header.set_custom_title(switcher_title) self.header.bind_property("title", switcher_title, "title", GObject.BindingFlags.SYNC_CREATE) self.header.bind_property("subtitle", switcher_title, "subtitle", GObject.BindingFlags.SYNC_CREATE) switcher_title.bind_property("title-visible", stack_switcher_bar, "reveal", GObject.BindingFlags.SYNC_CREATE) if self.authorized: self.show_main_page() else: self.show_login_page() vbox = Gtk.Box(orientation='vertical') vbox.pack_start(self.stack, True, True, 0) vbox.pack_end(stack_switcher_bar, False, False, 0) self.win.add(vbox) self.win.show_all() def handle_login(self, args, code): # TODO: should do some actual error handling here self.keyfile.set_string("GENERAL", "ACCESS_TOKEN", code) self.keyfile.save_to_file(self.config_file) pnutpy.api.add_authorization_token(code) self.authorized = True self.show_main_page() def show_login_page(self): login_page = LoginPage() login_page.connect("login", self.handle_login) self.stack.add_titled(login_page, "login", "Login") self.header.show_all() def show_main_page(self): login_page = self.stack.get_child_by_name("login") if login_page is not None: self.stack.remove(login_page) # TODO: find better icons unified = Timeline('unified') unified.connect('reply', self.on_reply_post) self.stack.add_titled(unified, "unified", "Timeline") self.stack.child_set_property(unified, "icon-name", "user-home-symbolic") mentions = Timeline('mentions') mentions.connect('reply', self.on_reply_post) self.stack.add_titled(mentions, "mentions", "Mentions") self.stack.child_set_property(mentions, "icon-name", "goa-panel-symbolic") bookmarks = Timeline('bookmarks') bookmarks.connect('reply', self.on_reply_post) self.stack.add_titled(bookmarks, "bookmarks", "Bookmarks") self.stack.child_set_property(bookmarks, "icon-name", "user-bookmarks-symbolic") global_tl = Timeline('global') global_tl.connect('reply', self.on_reply_post) self.stack.add_titled(global_tl, "global", "Global") self.stack.child_set_property(global_tl, "icon-name", "network-workgroup-symbolic") new_post_button = Gtk.Button.new_from_icon_name('list-add-symbolic', 1) new_post_button.connect('clicked', self.on_new_post) self.header.pack_start(new_post_button) menu = self.builder.get_object("app-menu") menu_button = Gtk.Button.new_from_icon_name('open-menu-symbolic', 1) menu_button.connect('clicked', self.on_main_popover) self.main_popover = Gtk.Popover.new_from_model(menu_button, menu) self.header.pack_end(menu_button) update_button = Gtk.Button.new_from_icon_name('view-refresh-symbolic', 1) update_button.connect('clicked', self.on_refresh) self.header.pack_end(update_button) self.header.show_all() self.stack.show_all() def on_new_post(self, widget, param=None): compose = ComposeWindow() compose.set_transient_for(self.win) def on_reply_post(self, widget, post_id, text): compose = ComposeWindow() compose.set_post(text) compose.set_reply_to(post_id) compose.set_transient_for(self.win) def on_main_popover(self, button): self.main_popover.popup() def on_refresh(self, action, param=None): timeline = self.stack.get_visible_child() timeline.emit('refresh') def on_top(self, action, param): timeline = self.stack.get_visible_child() timeline.emit('top') def on_preferences(self, action, param): pass def on_about(self, action, param): pass def on_quit(self, action, param): self.quit() def main(version): logging.basicConfig(level=logging.DEBUG) app = Application() return app.run(sys.argv)