formiko-1.3.0/0000755000175000017500000000000013227326227013353 5ustar mcbigmcbig00000000000000formiko-1.3.0/setup.cfg0000644000175000017500000000004613227326227015174 0ustar mcbigmcbig00000000000000[egg_info] tag_build = tag_date = 0 formiko-1.3.0/formiko-vim.desktop0000644000175000017500000000054613227323532017206 0ustar mcbigmcbig00000000000000[Desktop Entry] Type=Application Name=Formiko Vim Comment=reStructuredText and MarkDown editor and live html previewer - Vim mode Exec=formiko-vim %f Icon=formiko.png Terminal=false MimeType=text/plain;text/html;text/x-markdown;text/x-rst;application/json;text/json; Categories=Office;WordProcessor;GNOME;GTK; Keywords=Text;Editor;MarkDown;reStructuredText; formiko-1.3.0/MANIFEST.in0000644000175000017500000000027613227325352015114 0ustar mcbigmcbig00000000000000include *.py include MANIFEST.in include README.rst include COPYING include AUTHORS include ChangeLog include formiko.desktop include formiko-vim.desktop graft icons global-exclude *~ *.swp formiko-1.3.0/COPYING0000644000175000017500000000276013160721436014410 0ustar mcbigmcbig00000000000000BSD Licence ----------- Copyright (c) 2016, Formiko Team. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. formiko-1.3.0/formiko.desktop0000644000175000017500000000052313227323532016410 0ustar mcbigmcbig00000000000000[Desktop Entry] Type=Application Name=Formiko Comment=reStructuredText and MarkDown editor and live html previewer Exec=formiko %f Icon=formiko.png Terminal=false MimeType=text/plain;text/html;text/x-markdown;text/x-rst;application/json;text/json; Categories=Office;WordProcessor;GNOME;GTK; Keywords=Text;Editor;MarkDown;reStructuredText; formiko-1.3.0/formiko.egg-info/0000755000175000017500000000000013227326227016513 5ustar mcbigmcbig00000000000000formiko-1.3.0/formiko.egg-info/top_level.txt0000644000175000017500000000001013227326227021234 0ustar mcbigmcbig00000000000000formiko formiko-1.3.0/formiko.egg-info/requires.txt0000644000175000017500000000001713227326227021111 0ustar mcbigmcbig00000000000000docutils>=0.12 formiko-1.3.0/formiko.egg-info/PKG-INFO0000644000175000017500000001222713227326227017614 0ustar mcbigmcbig00000000000000Metadata-Version: 1.1 Name: formiko Version: 1.3.0 Summary: Formiko is reStructuredText and MarkDown editor and live previewer. Home-page: https://github.com/ondratu/formiko Author: Ondrej Tuma Author-email: mcbig@zeropage.cz License: BSD Description-Content-Type: UNKNOWN Description: Formiko ======= :author: Ondřej Tůma Formiko is reStructuredText and MarkDown editor and live previewer. It is written in Python with Gtk3, GtkSourceView and Webkit2. Use Docutils and recommonmark Common Mark parser. Features: --------- * GtkSourceView based editor with syntax highlighting * possible use Vim editor * vertical or horizontal window splitting * preview mode * periodic save file * json and html preview * spell check It support these parsers and writers: * Docutils reStructuredText parser - http://docutils.sourceforge.net * Common Mark parser - https://github.com/rtfd/recommonmark * Docutils HTML4, S5/HTML slide show and PEP HTML writer - http://docutils.sourceforge.net * Tiny HTML writer - https://github.com/ondratu/docutils-tinyhtmlwriter * Yet another HTML writer - https://github.com/masayuko/docutils-htmlwriter * HTML 5 writer - https://github.com/Kozea/docutils-html5-writer Vim support ~~~~~~~~~~~ Formiko have Vim editor support aka ``formiko-vim`` command. This run `Vim `_ editor in GtkSocket. At this moment, this socket work only on X11 backend, so this is not work on Wayland yet. There is bug for GTK+: Bug `721224 `_ - please add support for GtkSocket/GtkPlug in Wayland backend Requirements: ------------- * python 2.7 or 3 * GTK+3 * gobject-introspection * PyGObject * Webkit2 4.x * GtkSourceView 3.x * gir files for all Gtk libraries * GtkSpell3 recommended: ~~~~~~~~~~~~ * docutils - reStrucured support * recommonmark - for Common Mark support (MarkDown) * Pygments - syntax color in html output code blocks optionally: ~~~~~~~~~~~ **Python**: * docutils-tinyhtmlwriter * docutils-htmlwriter * docutils-html5-writer **System**: * vim-gtk or vim-gnome for ``formiko-vim`` Installation ------------ Debian ~~~~~~ Debian use sometimes versions in package names. Here are in Stretch version. If you use different version, your gtksource or webkit2 could have another version name. .. code:: sh # python3.5, gtk3, librsvg etc are in dependencies apt install python3-pip python3-gi python3-docutils gir1.2-gtksource-3.0 \ gir1.2-webkit2-4.0 gir1.2-gtkspell3-3.0 pip3 install formiko # optionaly apt install vim-gtk3 pip3 install docutils-tinyhtmlwriter recommonmark docutils-html5-writer Formiko is in Debian repository from Buster. So you can install it standard way. NetBSD ~~~~~~ There is not GtkSpell3 on NetBSD, which is need for next 1.3.x version. So you must use 1.2.x bug fix release. NetBSD use pkgsrc, so some binaries are stored in ``/usr/pkg/bin`` directory. Formiko call vim and gvim directly. If you want to use vim version with pkgsrc, you must fix ``VIM_PATH`` variable in ``formiko/vim.py`` file. .. code:: sh # python3.6 is in dependecies as like gtk3 pkgin install py36-pip py36-gobject3 py36-docutils gtksourceview3 \ librsvg webkit-gtk py36-pygments pip3.6 install formiko # optionaly pkgin install vim-gtk3 pip3.6 install docutils-tinyhtmlwriter recommonmark docutils-html5-writer Keywords: doc,html,rst,docutils,md,markdown,editor Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: X11 Applications :: GTK Classifier: Intended Audience :: Developers Classifier: Intended Audience :: End Users/Desktop Classifier: License :: OSI Approved :: BSD License Classifier: Natural Language :: English Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Topic :: Documentation Classifier: Topic :: Software Development :: Documentation Classifier: Topic :: Text Editors :: Documentation Classifier: Topic :: Text Editors :: Text Processing Classifier: Topic :: Text Processing Classifier: Topic :: Text Processing :: Markup Classifier: Topic :: Text Processing :: Markup :: HTML Classifier: Topic :: Utilities Requires: docutils (>= 0.12) Requires: python_gi Requires: webkit2 Requires: gtksourceview formiko-1.3.0/formiko.egg-info/dependency_links.txt0000644000175000017500000000000113227326227022561 0ustar mcbigmcbig00000000000000 formiko-1.3.0/formiko.egg-info/entry_points.txt0000644000175000017500000000011713227326227022010 0ustar mcbigmcbig00000000000000[gui_scripts] formiko = formiko.main:main formiko-vim = formiko.main:main_vim formiko-1.3.0/formiko.egg-info/SOURCES.txt0000644000175000017500000000155013227326227020400 0ustar mcbigmcbig00000000000000AUTHORS COPYING ChangeLog MANIFEST.in README.rst formiko-vim.desktop formiko.desktop setup.py formiko/__init__.py formiko/__main__.py formiko/application.py formiko/dialogs.py formiko/editor_actions.py formiko/icons.py formiko/main.py formiko/menu.py formiko/preferences.py formiko/renderer.py formiko/shortcuts.py formiko/sourceview.py formiko/status_menu.py formiko/user.py formiko/vim.py formiko/widgets.py formiko/window.py formiko.egg-info/PKG-INFO formiko.egg-info/SOURCES.txt formiko.egg-info/dependency_links.txt formiko.egg-info/entry_points.txt formiko.egg-info/requires.txt formiko.egg-info/top_level.txt icons/formiko.svg icons/svg2png.py icons/128x128/formiko.png icons/16x16/formiko.png icons/22x22/formiko.png icons/24x24/formiko.png icons/256x256/formiko.png icons/32x32/formiko.png icons/48x48/formiko.png icons/512x512/formiko.png icons/64x64/formiko.pngformiko-1.3.0/ChangeLog0000644000175000017500000000425213227323532015124 0ustar mcbigmcbig00000000000000Changelog ========= Version 1.3.0 - 16.1.2018 7:00 * spell check support * display white chars switch * highlighting current line switch * printing document output * shortcuts dialog Version 1.2.1 - 16.1.2018 6:53 * fix SIGSEGV - gui threads conflict * fix window cache settings * min size of editor and preview when both widgets are visible * python2 fix Version 1.2.0 - 26.10.2017 19:40 * vim with working application menu * realy disabled accels for vim * toggle buttons to on/off editor and preview * tabs setting in status bar * preferences are save automatical * auto indent is switchable * showing line numbers is switchable * right margin is switchable * text wrapping is switchable Version 1.1.0 - 03.08.2017 7:00 * GTK Version check * add accels only for SourceView editor * export document as HTML * using WebKit2 engine * Python 2.x encoding fixies * links, images and media urls showen in popup bar * JSON preview * PEP writer fix * only one application menu * info for Debian and NetBSD users * SourceView editor settings: - tab width - usinng spaces intead of tabs - 80 chars length line visualization - periodic saving file Version 1.0.2 - 19.07.2016 17:39 * Formiko Vim entry point Version 1.0.1 - 14.07.2016 09:08 * New 1.0.1 release * Adding ChangeLog file * fix vim editing on empty line at and of file * fix file path when application server is running Version 1.0.0 - 12.07.2016 08:24 * stable version 1.0.0 * right source distribution * desktop integration * litle bit fixies * application icon from petr simcik * final clean code * Small fixies from TODO: * html preview mode * clean text and new version * file type detection * working MarkDown support with gui * user preferences * modern Gtk+3 appearance (Menu) * opening file for sourceview editor * sourceview reading from and saving to files * base sourceview version * separating code to modules * preview mode * using Gtk.Application instead of GObject.GObject * full working * python3 support * crashing tread does crash gtk * using Gtk+3 * installable project * first based function formiko-1.3.0/PKG-INFO0000644000175000017500000001222713227326227014454 0ustar mcbigmcbig00000000000000Metadata-Version: 1.1 Name: formiko Version: 1.3.0 Summary: Formiko is reStructuredText and MarkDown editor and live previewer. Home-page: https://github.com/ondratu/formiko Author: Ondrej Tuma Author-email: mcbig@zeropage.cz License: BSD Description-Content-Type: UNKNOWN Description: Formiko ======= :author: Ondřej Tůma Formiko is reStructuredText and MarkDown editor and live previewer. It is written in Python with Gtk3, GtkSourceView and Webkit2. Use Docutils and recommonmark Common Mark parser. Features: --------- * GtkSourceView based editor with syntax highlighting * possible use Vim editor * vertical or horizontal window splitting * preview mode * periodic save file * json and html preview * spell check It support these parsers and writers: * Docutils reStructuredText parser - http://docutils.sourceforge.net * Common Mark parser - https://github.com/rtfd/recommonmark * Docutils HTML4, S5/HTML slide show and PEP HTML writer - http://docutils.sourceforge.net * Tiny HTML writer - https://github.com/ondratu/docutils-tinyhtmlwriter * Yet another HTML writer - https://github.com/masayuko/docutils-htmlwriter * HTML 5 writer - https://github.com/Kozea/docutils-html5-writer Vim support ~~~~~~~~~~~ Formiko have Vim editor support aka ``formiko-vim`` command. This run `Vim `_ editor in GtkSocket. At this moment, this socket work only on X11 backend, so this is not work on Wayland yet. There is bug for GTK+: Bug `721224 `_ - please add support for GtkSocket/GtkPlug in Wayland backend Requirements: ------------- * python 2.7 or 3 * GTK+3 * gobject-introspection * PyGObject * Webkit2 4.x * GtkSourceView 3.x * gir files for all Gtk libraries * GtkSpell3 recommended: ~~~~~~~~~~~~ * docutils - reStrucured support * recommonmark - for Common Mark support (MarkDown) * Pygments - syntax color in html output code blocks optionally: ~~~~~~~~~~~ **Python**: * docutils-tinyhtmlwriter * docutils-htmlwriter * docutils-html5-writer **System**: * vim-gtk or vim-gnome for ``formiko-vim`` Installation ------------ Debian ~~~~~~ Debian use sometimes versions in package names. Here are in Stretch version. If you use different version, your gtksource or webkit2 could have another version name. .. code:: sh # python3.5, gtk3, librsvg etc are in dependencies apt install python3-pip python3-gi python3-docutils gir1.2-gtksource-3.0 \ gir1.2-webkit2-4.0 gir1.2-gtkspell3-3.0 pip3 install formiko # optionaly apt install vim-gtk3 pip3 install docutils-tinyhtmlwriter recommonmark docutils-html5-writer Formiko is in Debian repository from Buster. So you can install it standard way. NetBSD ~~~~~~ There is not GtkSpell3 on NetBSD, which is need for next 1.3.x version. So you must use 1.2.x bug fix release. NetBSD use pkgsrc, so some binaries are stored in ``/usr/pkg/bin`` directory. Formiko call vim and gvim directly. If you want to use vim version with pkgsrc, you must fix ``VIM_PATH`` variable in ``formiko/vim.py`` file. .. code:: sh # python3.6 is in dependecies as like gtk3 pkgin install py36-pip py36-gobject3 py36-docutils gtksourceview3 \ librsvg webkit-gtk py36-pygments pip3.6 install formiko # optionaly pkgin install vim-gtk3 pip3.6 install docutils-tinyhtmlwriter recommonmark docutils-html5-writer Keywords: doc,html,rst,docutils,md,markdown,editor Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: X11 Applications :: GTK Classifier: Intended Audience :: Developers Classifier: Intended Audience :: End Users/Desktop Classifier: License :: OSI Approved :: BSD License Classifier: Natural Language :: English Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Topic :: Documentation Classifier: Topic :: Software Development :: Documentation Classifier: Topic :: Text Editors :: Documentation Classifier: Topic :: Text Editors :: Text Processing Classifier: Topic :: Text Processing Classifier: Topic :: Text Processing :: Markup Classifier: Topic :: Text Processing :: Markup :: HTML Classifier: Topic :: Utilities Requires: docutils (>= 0.12) Requires: python_gi Requires: webkit2 Requires: gtksourceview formiko-1.3.0/formiko/0000755000175000017500000000000013227326227015021 5ustar mcbigmcbig00000000000000formiko-1.3.0/formiko/application.py0000644000175000017500000001201213227323532017666 0ustar mcbigmcbig00000000000000from gi.repository.Gtk import Application as GtkApplication from gi.repository.GLib import OptionFlags, OptionArg, VariantType, \ log_default_handler, LogLevelFlags from gi.repository.Gio import ApplicationFlags, SimpleAction from gi.repository.Gdk import Display from traceback import print_exc from os.path import join from formiko.window import AppWindow from formiko.shortcuts import ShortcutsWindow from formiko.dialogs import AboutDialog, TraceBackDialog from formiko.menu import AppMenu class Application(GtkApplication): def __init__(self, application_id="cz.zeropage.formiko"): super(Application, self).__init__( application_id=application_id, flags=ApplicationFlags.HANDLES_COMMAND_LINE) self.add_main_option("preview", ord("p"), OptionFlags.NONE, OptionArg.NONE, "Preview only", None) self.add_main_option("vim", ord("v"), OptionFlags.NONE, OptionArg.NONE, "Use vim as editor", None) self.add_main_option("source-view", ord("s"), OptionFlags.NONE, OptionArg.NONE, "Use SourceView as editor (default)", None) def do_startup(self): GtkApplication.do_startup(self) action = SimpleAction.new("new-window", None) action.connect("activate", self.on_new_window) self.add_action(action) action = SimpleAction.new("shortcuts", None) action.connect("activate", self.on_shortcuts) self.add_action(action) action = SimpleAction.new("about", None) action.connect("activate", self.on_about) self.add_action(action) action = SimpleAction.new("traceback", VariantType.new('s')) action.connect("activate", self.on_traceback) self.add_action(action) action = SimpleAction.new("quit", None) action.connect("activate", self.on_quit) self.add_action(action) self.set_app_menu(AppMenu()) def do_activate(self): self.new_window() def do_command_line(self, command_line): options = command_line.get_options_dict() arguments = command_line.get_arguments()[1:] last = arguments[-1:][0] if arguments else '' if options.contains("vim"): log_default_handler("Application", LogLevelFlags.LEVEL_WARNING, "Use formiko-vim instead", 0) editor = 'vim' elif options.contains("source-view"): log_default_handler("Application", LogLevelFlags.LEVEL_WARNING, "Use formiko instead", 0) editor = 'source' else: editor = 'source' if self.get_application_id() == "cz.zeropage.formiko.vim": editor = 'vim' if editor == 'vim': display = Display.get_default() if display.__class__.__name__ != "X11Display": log_default_handler("Application", LogLevelFlags.LEVEL_ERROR, "Vim is supported only on X11 backend") if editor == 'source': # vim have disabled accels for conflict itself self.set_accels() if options.contains("preview") and last and last != '-': self.new_window(None, join(command_line.get_cwd(), last)) elif last and last[0] != '-': self.new_window(editor, join(command_line.get_cwd(), last)) else: self.new_window(editor) return 0 def on_quit(self, action, *params): self.quit() def on_new_window(self, action, *params): self.new_window(getattr(self.get_active_window(), 'editor_type', 'source')) def on_shortcuts(self, action, param): win = ShortcutsWindow(getattr(self.get_active_window(), 'editor_type', 'source')) self.add_window(win) win.show_all() def on_about(self, action, param): dialog = AboutDialog(None) dialog.present() def on_traceback(self, action, param): dialog = TraceBackDialog(self.get_active_window(), param.get_string()) dialog.present() def new_window(self, editor, file_name=''): try: win = AppWindow(editor, file_name) self.add_window(win) win.show_all() except BaseException: print_exc() def set_accels(self): self.set_accels_for_action("app.new-window", ["n"]) self.set_accels_for_action("app.quit", ["q"]) self.set_accels_for_action("win.open-document", ["o"]) self.set_accels_for_action("win.save-document", ["s"]) self.set_accels_for_action("win.save-document-as", ["s"]) self.set_accels_for_action("win.export-document-as", ["e"]) self.set_accels_for_action("win.print-document", ["p"]) self.set_accels_for_action("win.close-window", ["w"]) formiko-1.3.0/formiko/__init__.py0000644000175000017500000000040413227323532017124 0ustar mcbigmcbig00000000000000# -*- coding: utf-8 -*- __version__ = "1.3.0" __copyright__ = "(c) 2018" __author__ = "Ondřej Tůma " __url__ = "https://github.com/ondratu/formiko" __comment__ = \ "Formiko is reStructuredText and MarkDown editor and live previewer." formiko-1.3.0/formiko/menu.py0000644000175000017500000000162613227323532016340 0ustar mcbigmcbig00000000000000from gi.repository.Gio import Menu class AppMenu(Menu): def __init__(self): super(AppMenu, self).__init__() sec = Menu() sec.append("New Document", "app.new-window") sec.append("Open Document", "win.open-document") self.append_section(None, sec) sec = Menu() sec.append("Save Document", "win.save-document") sec.append("Save Document As...", "win.save-document-as") sec.append("Export Document As...", "win.export-document-as") sec.append("Print Document", "win.print-document") self.append_section(None, sec) sec = Menu() sec.append("Shortcuts", "app.shortcuts") sec.append("About Formiko", "app.about") self.append_section(None, sec) sec = Menu() sec.append("Close Document", "win.close-window") sec.append("Quit", "app.quit") self.append_section(None, sec) formiko-1.3.0/formiko/sourceview.py0000644000175000017500000001705213227323532017567 0ustar mcbigmcbig00000000000000from gi import require_version require_version('GtkSource', '3.0') # noqa require_version('Pango', '1.0') # noqa require_version('GtkSpell', '3.0') # noqa from gi.repository.Pango import FontDescription from gi.repository.GtkSource import LanguageManager, Buffer, View, \ DrawSpacesFlags from gi.repository.GLib import get_home_dir, timeout_add_seconds, Variant from gi.repository.GtkSpell import Checker from gi.repository import GObject from gi.repository import Gtk from os.path import splitext, basename, isfile from io import open from traceback import format_exc from sys import version_info from threading import Thread from formiko.dialogs import FileSaveDialog, TraceBackDialog from formiko.widgets import ActionHelper default_manager = LanguageManager.get_default() LANGS = { '.rst': default_manager.get_language('rst'), '.md': default_manager.get_language('markdown'), '.html': default_manager.get_language('html'), '.htm': default_manager.get_language('html'), '.json': default_manager.get_language('json') } PERIOD_SAVE_TIME = 300 # 5min class SourceView(Gtk.ScrolledWindow, ActionHelper): __file_name = '' __last_changes = 0 __gsignals__ = { 'file_type': (GObject.SIGNAL_RUN_FIRST, None, (str,)) } action_name = GObject.property(type=str) action_target = GObject.property(type=GObject.TYPE_VARIANT) def __init__(self, preferences, action_name=None): super(SourceView, self).__init__() if action_name: self.action_name = action_name self.set_hexpand(True) self.set_vexpand(True) self.text_buffer = Buffer.new_with_language( LANGS['.%s' % preferences.parser]) self.text_buffer.connect("changed", self.inc_changes) self.source_view = View.new_with_buffer(self.text_buffer) self.spellchecker = Checker() self.spellchecker.connect("language-changed", self.language_changed) self.source_view.override_font( FontDescription.from_string('Monospace')) # self.source_view.set_monospace(True) since 3.16 self.add(self.source_view) editor_pref = preferences.editor self.set_period_save(editor_pref.period_save) self.set_check_spelling(editor_pref.check_spelling, editor_pref.spell_lang) self.set_spaces_instead_of_tabs(editor_pref.spaces_instead_of_tabs) self.source_view.set_tab_width(editor_pref.tab_width) self.source_view.set_auto_indent(editor_pref.auto_indent) self.source_view.set_show_line_numbers(editor_pref.line_numbers) self.source_view.set_show_right_margin(editor_pref.right_margin) self.source_view.set_highlight_current_line(editor_pref.current_line) self.set_text_wrapping(editor_pref.text_wrapping) self.set_white_chars(editor_pref.white_chars) @property def changes(self): return self.__last_changes @property def is_modified(self): return self.text_buffer.get_modified() @property def text(self): return self.text_buffer.get_text(self.text_buffer.get_start_iter(), self.text_buffer.get_end_iter(), True) @property def position(self): cursor = self.text_buffer.get_insert() return self.text_buffer.get_iter_at_mark(cursor).get_offset() @property def file_name(self): return basename(self.__file_name) @property def file_path(self): return self.__file_name @property def file_ext(self): name, ext = splitext(self.__file_name) return ext def inc_changes(self, text_buffer): self.__last_changes += 1 def language_changed(self, spellchecker, language): action, go = self.get_action_owner() if go: action_target = Variant("s", language) go.activate_action(action, action_target) def set_period_save(self, save): self.period_save = bool(save)*PERIOD_SAVE_TIME if save: self.period_save_thread() def set_check_spelling(self, check_spelling, spell_lang): if check_spelling: if spell_lang in Checker.get_language_list(): self.spellchecker.set_language(spell_lang) else: # refresh from temporary off check spelling self.language_changed(self.spellchecker, self.spellchecker.get_language()) self.spellchecker.attach(self.source_view) else: self.spellchecker.detach() self.language_changed(self.spellchecker, "") def set_spaces_instead_of_tabs(self, use_spaces): self.source_view.set_insert_spaces_instead_of_tabs(use_spaces) self.source_view.set_smart_backspace(use_spaces) def set_text_wrapping(self, text_wrapping): if text_wrapping: self.source_view.set_wrap_mode(Gtk.WrapMode.WORD_CHAR) else: self.source_view.set_wrap_mode(Gtk.WrapMode.NONE) def set_white_chars(self, white_chars): if white_chars: self.source_view.set_draw_spaces(DrawSpacesFlags.ALL) else: self.source_view.set_draw_spaces(0) def period_save_thread(self): if self.period_save: if self.__file_name and self.is_modified: thread = Thread(target=self.save_to_file) thread.start() timeout_add_seconds(self.period_save, self.period_save_thread) def read_from_file(self, file_name): self.__file_name = file_name self.emit("file_type", self.file_ext) if isfile(file_name): with open(file_name, 'r', encoding="utf-8") as src: self.text_buffer.set_text(src.read()) self.__last_changes += 1 self.text_buffer.set_modified(False) def save_to_file(self, window=None): try: with open(self.__file_name, 'w', encoding="utf-8") as src: if version_info.major == 2: src.write(self.text.decode('utf-8')) else: # python version 3.x src.write(self.text) self.text_buffer.set_modified(False) except Exception: if window: md = TraceBackDialog(window, format_exc()) md.run() md.destroy() def save(self, window): if not self.__file_name: self.__file_name = self.get_new_file_name(window) self.emit("file_type", self.file_ext) if self.__file_name: self.save_to_file(window) def save_as(self, window): new_file_name = self.get_new_file_name(window) if new_file_name: self.__file_name = new_file_name self.emit("file_type", self.file_ext) self.save_to_file(window) def get_new_file_name(self, window): ret_val = '' dialog = FileSaveDialog(window) # TODO: set default filtry by select parser dialog.add_filter_rst() dialog.add_filter_md() dialog.add_filter_html() dialog.set_do_overwrite_confirmation(True) if not self.__file_name: dialog.set_current_folder(get_home_dir()) if dialog.run() == Gtk.ResponseType.ACCEPT: ret_val = dialog.get_filename() dialog.destroy() return ret_val def do_file_type(self, ext): language = LANGS.get(ext, LANGS['.rst']) if self.text_buffer.get_language() != language: self.text_buffer.set_language(language) formiko-1.3.0/formiko/editor_actions.py0000644000175000017500000001152613227323532020402 0ustar mcbigmcbig00000000000000from gi.repository.Gio import SimpleActionGroup, SimpleAction from gi.repository.GLib import VariantType, Variant class EditorActionGroup(SimpleActionGroup): def __init__(self, editor, renderer, preferences): super(EditorActionGroup, self).__init__() self.editor = editor self.renderer = renderer self.preferences = preferences self.editor_pref = preferences.editor self.create_stateful_action( "period-save-toggle", 'b', self.editor_pref.period_save, self.on_period_save) self.create_stateful_action( "check-spelling-toggle", 'b', self.editor_pref.check_spelling, self.on_check_spelling) action = SimpleAction.new("spell-lang", VariantType('s')) action.connect("activate", self.on_spell_lang) self.add_action(action) self.create_stateful_action( "use-spaces-toggle", 'b', self.editor_pref.spaces_instead_of_tabs, self.on_use_spaces) self.create_stateful_action( "tab-width", 'i', self.editor_pref.tab_width, self.on_tab_width) self.create_stateful_action( "auto-indent-toggle", 'b', self.editor_pref.auto_indent, self.on_auto_indent) self.create_stateful_action( "line-numbers-toggle", 'b', self.editor_pref.line_numbers, self.on_line_numbers) self.create_stateful_action( "right-margin-toggle", 'b', self.editor_pref.right_margin, self.on_right_margin) self.create_stateful_action( "current-line-toggle", 'b', self.editor_pref.current_line, self.on_current_line) self.create_stateful_action( "text-wrapping-toggle", 'b', self.editor_pref.text_wrapping, self.on_text_wrapping) self.create_stateful_action( "white-chars-toggle", 'b', self.editor_pref.white_chars, self.on_white_chars) def create_stateful_action(self, name, _type, default_value, method): action = SimpleAction.new_stateful( name, VariantType.new(_type), Variant(_type, default_value)) action.connect("change-state", method) self.add_action(action) def on_period_save(self, action, param): period_save = not self.editor_pref.period_save self.editor_pref.period_save = period_save self.editor.set_period_save(period_save) self.preferences.save() def on_check_spelling(self, action, param): check_spelling = not self.editor_pref.check_spelling self.editor_pref.check_spelling = check_spelling self.editor.set_check_spelling(check_spelling, self.editor_pref.spell_lang) self.preferences.save() def on_spell_lang(self, action, param): self.editor_pref.spell_lang = param.get_string() self.preferences.save() def on_use_spaces(self, action, param): use_spaces = not self.editor_pref.spaces_instead_of_tabs self.editor_pref.spaces_instead_of_tabs = use_spaces self.editor.set_spaces_instead_of_tabs(use_spaces) self.preferences.save() def on_tab_width(self, action, param): width = param.get_int32() self.editor_pref.tab_width = width self.editor.source_view.set_tab_width(width) self.renderer.set_tab_width(width) self.preferences.save() def on_auto_indent(self, action, param): auto_indent = not self.editor_pref.auto_indent self.editor_pref.auto_indent = auto_indent self.editor.source_view.set_auto_indent(auto_indent) self.preferences.save() def on_line_numbers(self, action, param): line_numbers = not self.editor_pref.line_numbers self.editor_pref.line_numbers = line_numbers self.editor.source_view.set_show_line_numbers(line_numbers) self.preferences.save() def on_right_margin(self, action, param): right_margin = not self.editor_pref.right_margin self.editor_pref.right_margin = right_margin self.editor.source_view.set_show_right_margin(right_margin) self.preferences.save() def on_current_line(self, action, param): current_line = not self.editor_pref.current_line self.editor_pref.current_line = current_line self.editor.source_view.set_highlight_current_line(current_line) self.preferences.save() def on_text_wrapping(self, action, param): text_wrapping = not self.editor_pref.text_wrapping self.editor_pref.text_wrapping = text_wrapping self.editor.set_text_wrapping(text_wrapping) self.preferences.save() def on_white_chars(self, action, param): white_chars = not self.editor_pref.white_chars self.editor_pref.white_chars = white_chars self.editor.set_white_chars(white_chars) self.preferences.save() formiko-1.3.0/formiko/user.py0000644000175000017500000001140513227323532016346 0ustar mcbigmcbig00000000000000from gi.repository.GLib import get_user_config_dir, get_user_cache_dir from gi.repository.Gtk import Orientation try: from configparser import ConfigParser, NoSectionError, NoOptionError except: from ConfigParser import ConfigParser, NoSectionError, NoOptionError from os import makedirs from os.path import exists from traceback import print_exc def smart_bool(value): if value.lower() in ("1", "true", "yes", "on", "enable"): return True elif value.lower() in ("0", "false", "no", "off", "disable"): return False raise ValueError("%s is not boolean value" % value) class SmartParser(ConfigParser): def smart_get(self, obj, key, conv=str, sec='main'): try: val = self.get(sec, key) setattr(obj, key, conv(val)) except NoSectionError: pass except NoOptionError: pass except Exception: print_exc() def smart_set(self, obj, key, sec='main'): self.set(sec, key, str(getattr(obj, key))) class EditorPreferences(object): period_save = True check_spelling = True spell_lang = "" spaces_instead_of_tabs = False tab_width = 8 auto_indent = True line_numbers = True right_margin = True current_line = False text_wrapping = True white_chars = False class UserPreferences(object): preview = Orientation.HORIZONTAL.numerator parser = 'rst' writer = 'html4' style = '' custom_style = False editor = EditorPreferences() def __init__(self): self.load() def load(self): directory = get_user_config_dir() cp = SmartParser() cp.read("%s/formiko.ini" % directory) cp.smart_get(self, 'preview', int) cp.smart_get(self, 'parser') cp.smart_get(self, 'writer') cp.smart_get(self, 'style') cp.smart_get(self, 'custom_style', smart_bool) cp.smart_get(self.editor, 'period_save', smart_bool, 'editor') cp.smart_get(self.editor, 'check_spelling', smart_bool, 'editor') cp.smart_get(self.editor, 'spell_lang', str, 'editor') cp.smart_get(self.editor, 'spaces_instead_of_tabs', smart_bool, 'editor') cp.smart_get(self.editor, 'tab_width', int, 'editor') cp.smart_get(self.editor, 'auto_indent', smart_bool, 'editor') cp.smart_get(self.editor, 'line_numbers', smart_bool, 'editor') cp.smart_get(self.editor, 'right_margin', smart_bool, 'editor') cp.smart_get(self.editor, 'current_line', smart_bool, 'editor') cp.smart_get(self.editor, 'text_wrapping', smart_bool, 'editor') cp.smart_get(self.editor, 'white_chars', smart_bool, 'editor') def save(self): cp = SmartParser() cp.add_section('main') cp.set('main', 'preview', str(int(self.preview))) cp.smart_set(self, 'parser') cp.smart_set(self, 'writer') cp.smart_set(self, 'style') cp.smart_set(self, 'custom_style') cp.add_section('editor') cp.smart_set(self.editor, 'period_save', 'editor') cp.smart_set(self.editor, 'check_spelling', 'editor') cp.smart_set(self.editor, 'spell_lang', 'editor') cp.smart_set(self.editor, 'spaces_instead_of_tabs', 'editor') cp.smart_set(self.editor, 'tab_width', 'editor') cp.smart_set(self.editor, 'auto_indent', 'editor') cp.smart_set(self.editor, 'line_numbers', 'editor') cp.smart_set(self.editor, 'right_margin', 'editor') cp.smart_set(self.editor, 'current_line', 'editor') cp.smart_set(self.editor, 'text_wrapping', 'editor') cp.smart_set(self.editor, 'white_chars', 'editor') directory = get_user_config_dir() if not exists(directory): makedirs(directory) with open("%s/formiko.ini" % directory, 'w+') as fp: cp.write(fp) class UserCache(object): width = 800 height = 600 paned = 400 is_maximized = False def __init__(self): self.load() def load(self): directory = get_user_cache_dir() cp = SmartParser() cp.read("%s/formiko/window.ini" % directory) cp.smart_get(self, 'width', int) cp.smart_get(self, 'height', int) cp.smart_get(self, 'paned', int) cp.smart_get(self, 'is_maximized', smart_bool) def save(self): cp = SmartParser() cp.add_section('main') cp.set('main', 'width', str(self.width)) cp.set('main', 'height', str(self.height)) cp.set('main', 'paned', str(self.paned)) cp.set('main', 'is_maximized', str(self.is_maximized)) directory = get_user_cache_dir()+"/formiko" if not exists(directory): makedirs(directory) with open("%s/window.ini" % directory, 'w+') as fp: cp.write(fp) formiko-1.3.0/formiko/dialogs.py0000644000175000017500000000762113160721435017017 0ustar mcbigmcbig00000000000000# -*- coding: utf-8 -*- from gi.repository.Pango import FontDescription from gi.repository import Gtk from formiko import __version__, __author__, __copyright__, __comment__ from formiko.icons import icon_list, icon_128 class AboutDialog(Gtk.AboutDialog): def __init__(self, transient_for): super(AboutDialog, self).__init__(transient_for=transient_for, modal=False) self.set_icon_list(icon_list) self.set_program_name("Formiko") self.set_version(__version__) self.set_copyright(__copyright__ + ' The Formiko Team') self.set_comments(__comment__) self.set_website("https://github.com/ondratu/formiko") self.set_license_type(Gtk.License.BSD) self.set_authors([__author__]) self.set_artists(["Petr Šimčík "]) self.set_logo(icon_128) class QuitDialogWithoutSave(Gtk.MessageDialog): def __init__(self, parent, file_name): super(QuitDialogWithoutSave, self).__init__( parent, Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.WARNING, Gtk.ButtonsType.OK_CANCEL, "File %s not saved.\n" "Are you sure to quite without save ?" % file_name) class TraceBackDialog(Gtk.Dialog): def __init__(self, parent, traceback): super(TraceBackDialog, self).__init__( "Traceback error", parent, Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, use_header_bar=True) box = self.get_content_area() label = Gtk.Label(traceback) label.override_font(FontDescription.from_string('Monospace')) label.show_all() box.add(label) class FileChooserDialog(Gtk.FileChooserDialog): def __init__(self, title, parent, action): if action == Gtk.FileChooserAction.SAVE: label = Gtk.STOCK_SAVE else: label = Gtk.STOCK_OPEN super(FileChooserDialog, self).__init__( title, parent, action, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, label, Gtk.ResponseType.ACCEPT)) def add_filter_rst(self): filter_rst = Gtk.FileFilter() filter_rst.set_name("reSructuredText") filter_rst.add_pattern("*.rst") filter_rst.add_pattern("*.RST") self.add_filter(filter_rst) def add_filter_md(self): filter_md = Gtk.FileFilter() filter_md.set_name("MarkDown") filter_md.add_pattern("*.md") filter_md.add_pattern("*.MD") filter_md.add_pattern("*.markdown") self.add_filter(filter_md) def add_filter_plain(self): filter_txt = Gtk.FileFilter() filter_txt.set_name("Plain text") filter_txt.add_mime_type("text/plain") self.add_filter(filter_txt) def add_filter_html(self): filter_html = Gtk.FileFilter() filter_html.extensions = ('.html', '.htm') filter_html.set_name("Hypertext files") filter_html.add_mime_type("text/html") self.add_filter(filter_html) def add_filter_json(self): filter_json = Gtk.FileFilter() filter_json.extensions = ('.json',) filter_json.set_name("JSON files") filter_json.add_mime_type("application/json") self.add_filter(filter_json) def add_filter_all(self): filter_all = Gtk.FileFilter() filter_all.set_name("all files") filter_all.add_pattern("*") self.add_filter(filter_all) class FileOpenDialog(FileChooserDialog): def __init__(self, parent): super(FileOpenDialog, self).__init__( "Open file", parent, Gtk.FileChooserAction.OPEN ) class FileSaveDialog(FileChooserDialog): def __init__(self, parent): super(FileSaveDialog, self).__init__( "Save as file", parent, Gtk.FileChooserAction.SAVE ) formiko-1.3.0/formiko/window.py0000644000175000017500000004164313227323532016706 0ustar mcbigmcbig00000000000000# -*- coding: utf-8 -*- from gi.repository import Gtk, GLib, Gio from threading import Thread from traceback import print_exc from os import stat from os.path import splitext from sys import version_info from io import open from formiko.vim import VimEditor from formiko.sourceview import SourceView from formiko.renderer import Renderer, EXTS from formiko.dialogs import QuitDialogWithoutSave, FileOpenDialog, \ FileSaveDialog from formiko.preferences import Preferences from formiko.user import UserCache, UserPreferences from formiko.icons import icon_list from formiko.status_menu import Statusbar from formiko.editor_actions import EditorActionGroup from formiko.widgets import IconButton NOT_SAVED_NAME = 'Not Saved Document' class View: EDITOR = 1 PREVIEW = 2 BOTH = 3 class AppWindow(Gtk.ApplicationWindow): def __init__(self, editor, file_name=''): assert editor in ('vim', 'source', None) self.runing = True self.editor_type = editor self.cache = UserCache() self.preferences = UserPreferences() super(AppWindow, self).__init__() self.create_renderer() self.actions() self.connect("delete-event", self.on_delete) self.set_titlebar(self.create_headerbar()) self.set_icon_list(icon_list) self.layout(file_name) self.__last_changes = 0 if self.editor_type is None: name, ext = splitext(file_name) self.on_file_type(None, ext) GLib.timeout_add(200, self.check_in_thread) def actions(self): action = Gio.SimpleAction.new("open-document", None) action.connect("activate", self.on_open_document) self.add_action(action) action = Gio.SimpleAction.new("save-document", None) action.connect("activate", self.on_save_document) action.set_enabled(False) self.add_action(action) action = Gio.SimpleAction.new("save-document-as", None) action.connect("activate", self.on_save_document_as) action.set_enabled(self.editor_type == 'source') self.add_action(action) action = Gio.SimpleAction.new("export-document-as", None) action.connect("activate", self.on_export_document_as) action.set_enabled(self.editor_type is not None) self.add_action(action) action = Gio.SimpleAction.new("print-document", None) action.connect("activate", self.on_print_document) self.add_action(action) action = Gio.SimpleAction.new("close-window", None) action.connect("activate", self.on_close_window) self.add_action(action) pref = self.preferences self.show_editor = True self.show_preview = True self.create_stateful_action( "switch-view-toggle", 'q', View.BOTH, self.on_switch_view_toggle) self.create_stateful_action( "change-preview", 'q', pref.preview, self.on_change_preview) self.create_stateful_action( "change-writer", 's', pref.writer, self.on_change_writer) self.create_stateful_action( "change-parser", 's', pref.parser, self.on_change_parser) self.create_stateful_action( "custom-style-toggle", 'b', pref.custom_style, self.on_custom_style_toggle) self.create_stateful_action( "change-style", 's', pref.style, self.on_change_style) def create_stateful_action(self, name, _type, default_value, method): action = Gio.SimpleAction.new_stateful( name, GLib.VariantType.new(_type), GLib.Variant(_type, default_value)) action.connect("change-state", method) self.add_action(action) def on_close_window(self, action, *params): if self.ask_if_modified(): self.save_win_state() self.destroy() def on_open_document(self, actions, *params): dialog = FileOpenDialog(self) dialog.add_filter_plain() dialog.add_filter_rst() dialog.add_filter_md() dialog.add_filter_html() dialog.add_filter_all() if dialog.run() == Gtk.ResponseType.ACCEPT: if self.editor_type == 'source' and \ self.get_title() == NOT_SAVED_NAME: self.editor.read_from_file(dialog.get_filename()) else: self.get_application().new_window(self.editor_type, dialog.get_filename()) dialog.destroy() def on_save_document(self, action, *params): if self.editor_type == 'source': self.editor.save(self) def on_save_document_as(self, action, *params): if self.editor_type == 'source': self.editor.save_as(self) def on_export_document_as(self, action, *params): file_name = self.editor.file_name or None dialog = FileSaveDialog(self) if self.renderer.get_parser() == 'json': dialog.add_filter_json() else: dialog.add_filter_html() dialog.add_filter_all() dialog.set_do_overwrite_confirmation(True) if file_name is None: dialog.set_current_folder(GLib.get_home_dir()) else: dialog.set_current_name(file_name[:file_name.rfind('.')]) if dialog.run() == Gtk.ResponseType.ACCEPT: extensions = getattr(dialog.get_filter(), 'extensions', ()) file_name = dialog.get_filename() ex_ok = False for extension in extensions: if file_name.lower().endswith(extension): ex_ok = True break if not ex_ok and extensions: file_name += extensions[0] with open(file_name, "w+", encoding="utf-8") as output: data = self.renderer.render_output()[1].strip() if version_info.major == 2: output.write(data.encode("utf-8")) else: # python 3.x output.write(data) dialog.destroy() def on_print_document(self, action, *params): self.renderer.print_page() def on_delete(self, *args): rv = self.ask_if_modified() if rv: self.save_win_state() return not rv def on_switch_view_toggle(self, action, param): if action.get_state() != param: action.set_state(param) state = param.get_uint16() if state == View.BOTH: self.editor.show() self.renderer.show() self.both_toggle_btn.set_active(True) elif state == View.EDITOR: self.editor.show() self.renderer.hide() self.editor_toggle_btn.set_active(True) else: self.editor.hide() self.renderer.show() self.preview_toggle_btn.set_active(True) def on_change_preview(self, action, param): if action.get_state() != param: action.set_state(param) if not getattr(self, 'paned', False): return orientation = param.get_uint16() if self.paned.get_orientation() != orientation: self.paned.set_orientation(orientation) if orientation == Gtk.Orientation.HORIZONTAL: self.paned.set_position(self.paned.get_allocated_width()/2) else: self.paned.set_position(self.paned.get_allocated_height()/2) self.preferences.preview = orientation self.preferences.save() def on_change_parser(self, action, param): if action.get_state() != param: action.set_state(param) parser = param.get_string() self.renderer.set_parser(parser) self.preferences.parser = parser self.preferences.save() def on_file_type(self, widget, ext): parser = EXTS.get(ext, self.preferences.parser) self.pref_menu.set_parser(parser) def on_change_writer(self, action, param): if action.get_state() != param: action.set_state(param) writer = param.get_string() self.renderer.set_writer(writer) self.preferences.writer = writer self.preferences.save() def on_custom_style_toggle(self, action, param): custom_style = not self.preferences.custom_style self.preferences.custom_style = custom_style if custom_style and self.preferences.style: self.renderer.set_style(self.preferences.style) else: self.renderer.set_style('') self.preferences.save() def on_change_style(self, action, param): style = param.get_string() self.preferences.style = style if self.preferences.custom_style and style: self.renderer.set_style(self.preferences.style) else: self.renderer.set_style('') self.preferences.save() def ask_if_modified(self): if self.editor_type: if self.editor.is_modified: dialog = QuitDialogWithoutSave(self, self.editor.file_name) if dialog.run() != Gtk.ResponseType.OK: dialog.destroy() return False # fo not quit self.runing = False if self.editor_type == 'vim': self.editor.vim_quit() # do call destroy_from_vim else: self.runing = False return True # do quit def destroy_from_vim(self, *args): self.runing = False self.destroy() def save_win_state(self): self.cache.width, self.cache.height = self.get_size() if getattr(self, 'paned', False): self.cache.paned = self.paned.get_position() self.cache.is_maximized = self.is_maximized() self.cache.save() def create_headerbar(self): headerbar = Gtk.HeaderBar() headerbar.set_show_close_button(True) headerbar.pack_start(IconButton(symbol="document-new-symbolic", tooltip="New Document", action_name="app.new-window")) headerbar.pack_start(IconButton(symbol="document-open-symbolic", tooltip="Open Document", action_name="win.open-document")) if self.editor_type == 'source': headerbar.pack_start(IconButton(symbol="document-save-symbolic", tooltip="Save Document", action_name="win.save-document")) self.pref_menu = Preferences(self.preferences) btn = Gtk.MenuButton(popover=self.pref_menu) icon = Gio.ThemedIcon(name="emblem-system-symbolic") btn.add(Gtk.Image.new_from_gicon(icon, Gtk.IconSize.BUTTON)) btn.set_tooltip_text("Preferences") headerbar.pack_end(btn) if self.editor_type != 'preview': btn_box = Gtk.ButtonBox.new(orientation=Gtk.Orientation.HORIZONTAL) Gtk.StyleContext.add_class(btn_box.get_style_context(), "linked") self.editor_toggle_btn = Gtk.ToggleButton( label="Editor", action_name="win.switch-view-toggle", action_target=GLib.Variant('q', View.EDITOR)) self.editor_toggle_btn.set_tooltip_text("Show Editor") btn_box.pack_start(self.editor_toggle_btn, True, True, 0) self.preview_toggle_btn = Gtk.ToggleButton( label="Preview", action_name="win.switch-view-toggle", action_target=GLib.Variant('q', View.PREVIEW)) self.preview_toggle_btn.set_tooltip_text("Show Web Preview") btn_box.pack_start(self.preview_toggle_btn, True, True, 0) self.both_toggle_btn = Gtk.ToggleButton( label="Both", action_name="win.switch-view-toggle", action_target=GLib.Variant('q', View.BOTH)) self.both_toggle_btn.set_tooltip_text( "Show Editor and Web Preview") btn_box.pack_start(self.both_toggle_btn, True, True, 0) headerbar.pack_end(btn_box) return headerbar def create_renderer(self): self.renderer = Renderer(self, parser=self.preferences.parser, writer=self.preferences.writer) if self.preferences.custom_style and self.preferences.style: self.renderer.set_style(self.preferences.style) self.renderer.set_tab_width(self.preferences.editor.tab_width) def fill_panned(self, file_name): if self.editor_type == 'vim': self.editor = VimEditor(self, file_name) self.editor.connect("file_type", self.on_file_type) else: self.editor = SourceView(self.preferences, "editor.spell-lang") self.insert_action_group("editor", EditorActionGroup(self.editor, self.renderer, self.preferences)) self.editor.connect("file_type", self.on_file_type) if file_name: self.editor.read_from_file(file_name) self.paned.pack1(self.editor, True, False) self.paned.pack2(self.renderer, True, False) def layout(self, file_name): self.set_default_size(self.cache.width, self.cache.height) box = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0) self.add(box) if self.editor_type: self.paned = Gtk.Paned(orientation=self.preferences.preview, position=self.cache.paned) box.pack_start(self.paned, True, True, 0) self.fill_panned(file_name) else: self.file_name = file_name self.set_title(file_name) box.pack_start(self.renderer, True, True, 0) if self.cache.is_maximized: self.maximize() if self.editor_type == 'source': box.pack_end(Statusbar(self.preferences.editor), False, True, 0) def check_in_thread(self): if self.runing: if self.editor_type == 'vim': thread = Thread(target=self.refresh_from_vim) thread.start() elif self.editor_type == 'source': GLib.idle_add(self.refresh_from_source) else: # self.editor = None GLib.idle_add(self.refresh_from_file) def not_running(self): if not self.runing: raise SystemExit(0) def refresh_from_vim(self): another_file = False try: star = '*' if self.editor.is_modified else '' self.not_running() title = star + (self.editor.file_name or NOT_SAVED_NAME) if title != self.get_title(): GLib.idle_add(self.set_title, title) another_file = True self.not_running() last_changes = self.editor.get_vim_changes() if last_changes > self.__last_changes or another_file: self.__last_changes = last_changes self.not_running() lines = self.editor.get_vim_lines() self.not_running() buff = self.editor.get_vim_get_buffer(lines) self.not_running() row, col = self.editor.get_vim_pos() pos = 0 for i in range(row-1): new_line = buff.find('\n', pos) if new_line < 0: break pos = new_line + 1 pos += col self.renderer.render(buff, self.editor.file_path, pos) GLib.timeout_add(300, self.check_in_thread) except SystemExit: return except BaseException: print_exc() def refresh_from_source(self): try: modified = self.editor.is_modified self.lookup_action("save-document").set_enabled(modified) star = '*' if modified else '' title = star + (self.editor.file_name or NOT_SAVED_NAME) if title != self.get_title(): self.set_title(title) last_changes = self.editor.changes if last_changes > self.__last_changes: self.__last_changes = last_changes self.renderer.render(self.editor.text, self.editor.file_path, self.editor.position) GLib.timeout_add(100, self.check_in_thread) except BaseException: print_exc() def refresh_from_file(self): try: last_changes = stat(self.file_name).st_ctime if last_changes > self.__last_changes: self.__last_changes = last_changes with open(self.file_name) as source: buff = source.read() self.renderer.render(buff, self.file_name) except BaseException: print_exc() GLib.timeout_add(500, self.check_in_thread) formiko-1.3.0/formiko/icons.py0000644000175000017500000000321413160721435016502 0ustar mcbigmcbig00000000000000# -*- coding: utf-8 -*- from gi.repository.GdkPixbuf import Pixbuf from os.path import exists, split, join from sys import argv def get_path(): """Try to get path to formiko.svg""" bin_path = split(argv[0])[0] icons_path = "icons/formiko.svg" share_path = join("share/formiko", icons_path) while (bin_path != '/'): # this work to /opt /usr /usr/local prefixes if exists(join(bin_path, share_path)): return join(bin_path, share_path) if exists(join(bin_path, icons_path)): return join(bin_path, icons_path) bin_path = split(bin_path)[0] ICON_PATH = get_path() if ICON_PATH: icon_16 = Pixbuf.new_from_file_at_scale(ICON_PATH, 16, 16, True) icon_32 = Pixbuf.new_from_file_at_scale(ICON_PATH, 32, 32, True) icon_48 = Pixbuf.new_from_file_at_scale(ICON_PATH, 48, 48, True) icon_64 = Pixbuf.new_from_file_at_scale(ICON_PATH, 64, 64, True) icon_128 = Pixbuf.new_from_file_at_scale(ICON_PATH, 128, 128, True) else: from gi.repository.Gtk import IconTheme from gi.repository.GLib import log_default_handler, LogLevelFlags log_default_handler("Application", LogLevelFlags.LEVEL_ERROR, "Formiko icon not found", 0) icon_theme = IconTheme.get_default() icon_16 = icon_theme.load_icon("text-editor-symbolic", 16, 0) icon_32 = icon_theme.load_icon("text-editor-symbolic", 32, 0) icon_48 = icon_theme.load_icon("text-editor-symbolic", 48, 0) icon_64 = icon_theme.load_icon("text-editor-symbolic", 64, 0) icon_128 = icon_theme.load_icon("text-editor-symbolic", 128, 0) icon_list = [icon_16, icon_32, icon_48, icon_64, icon_128] formiko-1.3.0/formiko/__main__.py0000644000175000017500000000005413160721435017106 0ustar mcbigmcbig00000000000000from formiko.main import main exit(main()) formiko-1.3.0/formiko/status_menu.py0000644000175000017500000001443413227323532017744 0ustar mcbigmcbig00000000000000from gi.repository import Gtk from gi.repository import Gio from gi.repository import GLib class StatusMenuButton(Gtk.MenuButton): css = Gtk.CssProvider() css.load_from_data(b""" * { border: 0; padding: 1px 8px 2px 4px; outline-width: 0; } """) def __init__(self, label, popover): super(StatusMenuButton, self).__init__(popover=popover) self.set_relief(Gtk.ReliefStyle.NONE) ctx = self.get_style_context() ctx.add_provider(StatusMenuButton.css, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) box = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 0) self.add(box) self.label = Gtk.Label(label) box.pack_start(self.label, True, True, 0) icon = Gio.ThemedIcon(name="pan-down-symbolic") box.pack_start(Gtk.Image.new_from_gicon(icon, Gtk.IconSize.MENU), True, True, 0) def set_label(self, label): self.label.set_label(label) # endclass class LineColPopover(Gtk.Popover): def __init__(self, preferences): super(LineColPopover, self).__init__() self.box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, border_width=10) self.add(self.box) self.add_check_button( 'Display line numbers', "editor.line-numbers-toggle", preferences.line_numbers) self.add_check_button( 'Display right margin', "editor.right-margin-toggle", preferences.right_margin) self.add_check_button( 'Highlight current line', "editor.current-line-toggle", preferences.current_line) self.add_check_button( 'Text wrapping', "editor.text-wrapping-toggle", preferences.text_wrapping) self.add_check_button( 'Draw white chars', "editor.white-chars-toggle", preferences.white_chars) self.box.show_all() def add_check_button(self, label, action, value): btn = Gtk.CheckButton( label=label, action_name=action, action_target=GLib.Variant('b', True)) btn.set_active(value) self.box.pack_start(btn, True, True, 0) # endclass class Statusbar(Gtk.Statusbar): css = Gtk.CssProvider() css.load_from_data(b"* {border-top: 1px solid #91918c;}") def __init__(self, preferences): super(Statusbar, self).__init__() self.set_margin_top(0) self.set_margin_bottom(0) self.set_margin_start(0) self.set_margin_end(0) ctx = self.get_style_context() ctx.add_provider(Statusbar.css, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) self.editor_popover = self.create_editor_popover(preferences) self.editor_btn = StatusMenuButton( "Editor", self.editor_popover) self.pack_start(self.editor_btn, False, False, 1) self.tab_popover = self.create_tab_popover(preferences) self.width_btn = StatusMenuButton( "Tabulator width %d" % preferences.tab_width, self.tab_popover) self.pack_start(self.width_btn, False, False, 1) btn = StatusMenuButton( "Line 1, Col 1", LineColPopover(preferences)) self.pack_start(btn, False, False, 1) def create_tab_popover(self, preferences): pop = Gtk.Popover() box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, border_width=10) pop.add(box) auto_indent = Gtk.CheckButton( label="Auto indent", action_name="editor.auto-indent-toggle", action_target=GLib.Variant('b', True)) box.pack_start(auto_indent, True, True, 0) box.pack_start(Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL), True, True, 5) tab_spaces_2 = Gtk.RadioButton( label="2", action_name="editor.tab-width", action_target=GLib.Variant('i', 2)) tab_spaces_2.connect("toggled", self.on_tab_spaces) if preferences.tab_width == 2: tab_spaces_2.set_active(True) box.pack_start(tab_spaces_2, True, True, 0) tab_spaces_4 = Gtk.RadioButton( label="4", action_name="editor.tab-width", action_target=GLib.Variant('i', 4), group=tab_spaces_2) tab_spaces_4.connect("toggled", self.on_tab_spaces) if preferences.tab_width == 4: tab_spaces_2.set_active(True) box.pack_start(tab_spaces_4, True, True, 0) tab_spaces_8 = Gtk.RadioButton( label="8", action_name="editor.tab-width", action_target=GLib.Variant('i', 8), group=tab_spaces_2) tab_spaces_8.connect("toggled", self.on_tab_spaces) if preferences.tab_width == 8: tab_spaces_2.set_active(True) box.pack_start(tab_spaces_8, True, True, 0) box.pack_start(Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL), True, True, 5) self.tab_use_space = Gtk.CheckButton( label="Use spaces", action_name="editor.use-spaces-toggle", action_target=GLib.Variant('b', True)) box.pack_start(self.tab_use_space, True, True, 0) box.show_all() return pop def on_tab_spaces(self, widget): if widget.get_active(): self.width_btn.set_label("Tabulator width %s" % widget.get_action_target_value()) def create_editor_popover(self, preferences): pop = Gtk.Popover() box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, border_width=10) pop.add(box) period_btn = Gtk.CheckButton( label='Save file each 5 min', action_name="editor.period-save-toggle", action_target=GLib.Variant('b', True)) period_btn.set_active(preferences.period_save) box.pack_start(period_btn, True, True, 0) spell_btn = Gtk.CheckButton( label='Check Spelling', action_name="editor.check-spelling-toggle", action_target=GLib.Variant('b', True)) spell_btn.set_active(preferences.check_spelling) box.pack_start(spell_btn, True, True, 0) box.show_all() return pop formiko-1.3.0/formiko/widgets.py0000644000175000017500000000123213227323532017033 0ustar mcbigmcbig00000000000000from gi.repository import Gtk from gi.repository.Gio import ThemedIcon class IconButton(Gtk.Button): def __init__(self, symbol, tooltip, **kwargs): super(IconButton, self).__init__(**kwargs) icon = ThemedIcon(name=symbol) self.add(Gtk.Image.new_from_gicon(icon, Gtk.IconSize.BUTTON)) self.set_tooltip_text(tooltip) class ActionHelper(object): def get_action_owner(self): if self.action_name: prefix, action = self.action_name.split('.') go = self.get_toplevel().get_action_group(prefix) if go and go.has_action(action): return action, go return '', None formiko-1.3.0/formiko/shortcuts.py0000644000175000017500000000617513227323532017436 0ustar mcbigmcbig00000000000000# -*- coding: utf-8 -*- from gi.repository.Gtk import ShortcutsWindow, ShortcutsSection, \ ShortcutsGroup, ShortcutsShortcut class SourceGroup(ShortcutsGroup): def __init__(self): super(SourceGroup, self).__init__(title="Editor") self.add(ShortcutsShortcut( accelerator="c", title="Copy")) self.add(ShortcutsShortcut( accelerator="x", title="Cut")) self.add(ShortcutsShortcut( accelerator="v", title="Paste")) self.add(ShortcutsShortcut( accelerator="a", title="Select All")) self.add(ShortcutsShortcut( accelerator="Home", title="Go to Begin of Document")) self.add(ShortcutsShortcut( accelerator="End", title="Go to End of Document")) class VimGroup(ShortcutsGroup): def __init__(self): super(VimGroup, self).__init__(title="Vim") self.add(ShortcutsShortcut( accelerator="y", title="Copy")) self.add(ShortcutsShortcut( accelerator="x", title="Cut")) self.add(ShortcutsShortcut( accelerator="p", title="Paste")) self.add(ShortcutsShortcut( accelerator="Escape+g+g", title="Go to Begin of Document")) self.add(ShortcutsShortcut( accelerator="Escape+G", title="Go to End of Document")) self.add(ShortcutsShortcut( accelerator="Escape+g+g+v+G", title="Select All")) class GeneralGroup(ShortcutsGroup): def __init__(self, editor_type): super(GeneralGroup, self).__init__(title="Genaral") self.add(ShortcutsShortcut( accelerator="n", title="New Document")) self.add(ShortcutsShortcut( accelerator="o", title="Open Document")) if editor_type == "source": self.add(ShortcutsShortcut( accelerator="s", title="Save Document")) self.add(ShortcutsShortcut( accelerator="s", title="Save Document As")) elif editor_type == "vim": self.add(ShortcutsShortcut( accelerator="Escape+colon+w", title="Save Document Vim")) self.add(ShortcutsShortcut( accelerator="e", title="Export Document As")) self.add(ShortcutsShortcut( accelerator="p", title="Print Document")) self.add(ShortcutsShortcut( accelerator="w", title="Close Document")) self.add(ShortcutsShortcut( accelerator="q", title="Quit Formiko")) class ShortcutsWindow(ShortcutsWindow): def __init__(self, editor_type): # view_name and view does not work. Don't know why super(ShortcutsWindow, self).__init__(modal=1) sec = ShortcutsSection(title="Formiko", visible=True) general = GeneralGroup(editor_type) sec.add(general) if editor_type == "source": source = SourceGroup() sec.add(source) elif editor_type == "vim": vim = VimGroup() sec.add(vim) self.add(sec) formiko-1.3.0/formiko/main.py0000644000175000017500000000114113167057167016323 0ustar mcbigmcbig00000000000000from gi import require_version require_version('Gdk', '3.0') # noqa require_version('Gtk', '3.0') # noqa from gi.repository import Gdk from sys import argv from signal import signal, SIGINT from formiko.application import Application def handler_exit(*args): exit(1) def main(): signal(SIGINT, handler_exit) Gdk.threads_init() app = Application() return app.run(argv) def main_vim(): signal(SIGINT, handler_exit) Gdk.threads_init() app = Application(application_id="cz.zeropage.formiko.vim") return app.run(argv) if __name__ == "__main__": exit(main()) formiko-1.3.0/formiko/vim.py0000644000175000017500000000703613162410317016164 0ustar mcbigmcbig00000000000000# -*- coding: utf-8 -*- from gi.repository.Gtk import Socket from gi.repository.GObject import SIGNAL_RUN_FIRST from subprocess import Popen, PIPE, check_output from logging import error from os.path import splitext from uuid import uuid4 from time import sleep VIM_PATH = "/usr/bin" class VimEditor(Socket): __gsignals__ = { 'file_type': (SIGNAL_RUN_FIRST, None, (str,)) } def __init__(self, app_window, file_name=''): super(VimEditor, self).__init__() self.__file_name = file_name self.__server_name = str(uuid4()) self.connect("plug-removed", app_window.destroy_from_vim) self.connect("realize", self.print_state) def print_state(self, *args): self.vim_start_server() def vim_start_server(self): if self.__file_name: name, ext = splitext(self.__file_name) self.emit("file_type", ext) file_type = "" else: file_type = " filetype=rst" args = [ VIM_PATH+"/gvim", "--socketid", str(self.get_id()), "--servername", self.__server_name, "--echo-wid", # no menu (m) a no toolbar (T) "-c", "set go-=m go-=T" + file_type] if self.__file_name: args.append(self.__file_name) server = Popen(args, stdout=PIPE) server.stdout.readline() # read wid, so server was started sleep(0.1) # some time for vim server def vim_remote_expr(self, command): out = check_output([ VIM_PATH+"/vim", "--servername", self.__server_name, "--remote-expr", command ]) return out.decode('utf-8').strip() def vim_remote_send(self, command): check_output([ VIM_PATH+"/vim", "--servername", self.__server_name, "--remote-send", command ]) def get_vim_changes(self): return int(self.vim_remote_expr("b:changedtick") or '0') def get_vim_lines(self): return int(self.vim_remote_expr("line('$')") or '0') def get_vim_get_buffer(self, count): return self.vim_remote_expr("getline(0, %d)" % count) def get_vim_pos(self): pos = self.vim_remote_expr("getpos('.')") or ',0,0,' buff, row, col, off = pos.split('\n') return int(row), int(col) def get_vim_file_path(self): return self.vim_remote_expr("expand('%:p')") def get_vim_encoding(self): return self.vim_remote_expr("&l:encoding") def get_vim_filetype(self): return self.vim_remote_expr("&l:filetype") def vim_quit(self): self.vim_remote_send(":q! ") def vim_open_file(self, file_name): self.vim_remote_send(":e %s" % file_name) @property def is_modified(self): return bool(int(self.vim_remote_expr("&l:modified") or '0')) @property def file_name(self): __file_name = self.vim_remote_expr("@%") if __file_name != self.__file_name: name, ext = splitext(__file_name) self.emit("file_type", ext) self.__file_name = __file_name return self.__file_name @property def file_path(self): return self.get_vim_file_path() def do_file_type(self, ext): pass def read_from_file(self, file_name): error('Not supported call read_from_file in VimEditor') def save(self, *args): error('Not supported call save in VimEditor') def save_as(self, *args): error('Not supported call save_as in VimEditor') formiko-1.3.0/formiko/renderer.py0000644000175000017500000002224613227323532017203 0ustar mcbigmcbig00000000000000# -*- coding: utf-8 -*- from gi import require_version require_version('WebKit2', '4.0') # noqa from gi.repository.WebKit2 import WebView, PrintOperation from gi.repository.GLib import idle_add, Bytes, get_home_dir, \ log_default_handler, LogLevelFlags from gi.repository.Gtk import ScrolledWindow, PolicyType, Overlay, Label, \ Align from docutils import DataError from docutils.core import publish_string from docutils.parsers.rst import Parser as RstParser from docutils.writers.html4css1 import Writer as Writer4css1 from docutils.writers.s5_html import Writer as WriterS5 from docutils.writers.pep_html import Writer as WriterPep try: from docutils_tinyhtml import Writer as TinyWriter except ImportError: TinyWriter = None try: from htmlwriter import Writer as HtmlWriter except ImportError: HtmlWriter = None try: from docutils_html5 import Writer as Html5Writer except ImportError: Html5Writer = None try: from recommonmark.parser import CommonMarkParser except ImportError: CommonMarkParser = None from io import StringIO from traceback import format_exc from sys import version_info from json import loads, dumps class HtmlPreview(object): """Dummy html preview class""" pass class JSONPreview(object): """Dummy json preview class""" pass PARSERS = { 'rst': { 'key': 'rst', 'title': 'Docutils reStructuredText parser', 'class': RstParser, 'url': 'http://docutils.sourceforge.net'}, 'md': { 'key': 'md', 'title': 'Common Mark parser', 'class': CommonMarkParser, 'url': 'https://github.com/rtfd/recommonmark'}, 'html': { 'key': 'html', 'title': 'HTML preview', 'class': HtmlPreview}, 'json': { 'key': 'json', 'title': 'JSON preview', 'class': JSONPreview} } EXTS = { '.rst': 'rst', '.md': 'md', '.html': 'html', '.htm': 'html', '.json': 'json' } WRITERS = { 'html4': { 'key': 'html4', 'title': 'Docutils HTML4 writer', 'class': Writer4css1, 'url': 'http://docutils.sourceforge.net'}, 's5': { 'key': 's5', 'title': 'Docutils S5/HTML slide show writer', 'class': WriterS5, 'url': 'http://docutils.sourceforge.net'}, 'pep': { 'key': 'pep', 'title': 'Docutils PEP HTML writer', 'class': WriterPep, 'url': 'http://docutils.sourceforge.net'}, 'tiny': { 'key': 'tiny', 'title': 'Tiny HTML writer', 'class': TinyWriter, 'url': 'https://github.com/ondratu/docutils-tinyhtmlwriter'}, 'html': { 'key': 'html', 'title': 'Yet another HTML writer', 'class': HtmlWriter, 'url': 'https://github.com/masayuko/docutils-htmlwriter'}, 'html5': { 'key': 'html5', 'title': 'HTML 5 writer', 'class': Html5Writer, 'url': 'https://github.com/Kozea/docutils-html5-writer'}, } NOT_FOUND = """

Commponent {title} Not Found!

Component {title} which you want to use is not found. See {url} for mor details and install it to system.

""" DATA_ERROR = """

%s Error!

%s

""" EXCEPTION_ERROR = """

Exception Error!

%s
""" SCROLL = """ """ MARKUP = """ %s """ class Renderer(Overlay): def __init__(self, win, parser='rst', writer='html4', style=''): super(Renderer, self).__init__() scrolled = ScrolledWindow.new(None, None) scrolled.set_policy(PolicyType.AUTOMATIC, PolicyType.AUTOMATIC) self.sb = scrolled.get_vscrollbar() self.add(scrolled) self.webview = WebView() self.webview.connect("mouse-target-changed", self.on_mouse) scrolled.add(self.webview) self.label = Label() self.label.set_halign(Align.START) self.label.set_valign(Align.END) self.add_overlay(self.label) self.set_writer(writer) self.set_parser(parser) self.style = style self.tab_width = 8 self.__win = win def on_mouse(self, web_view, hit_test_result, modifiers): if hit_test_result.context_is_link(): text = "link: %s" % hit_test_result.get_link_uri() elif hit_test_result.context_is_image(): text = "image: %s" % hit_test_result.get_image_uri() elif hit_test_result.context_is_media(): text = "media: %s" % hit_test_result.get_media_uri() else: if self.label.is_visible(): self.label.hide() return self.label.set_markup(MARKUP % text) self.label.show() def set_writer(self, writer): assert writer in WRITERS self.__writer = WRITERS[writer] klass = self.__writer['class'] self.writer_instance = klass() if klass is not None else None idle_add(self.do_render) def get_writer(self): return self.__writer['key'] def set_parser(self, parser): assert parser in PARSERS self.__parser = PARSERS[parser] klass = self.__parser['class'] self.parser_instance = klass() if klass is not None else None idle_add(self.do_render) def get_parser(self): return self.__parser['key'] def set_style(self, style): self.style = style idle_add(self.do_render) def get_style(self): return self.style def set_tab_width(self, width): self.tab_width = width def render_output(self): if getattr(self, 'src', None) is None: return False, "", "text/plain" try: if self.__parser['class'] is None: html = NOT_FOUND.format(**self.__parser) elif self.__writer['class'] is None: html = NOT_FOUND.format(**self.__writer) elif issubclass(self.__parser['class'], JSONPreview): try: json = loads(self.src) return (False, dumps(json, sort_keys=True, ensure_ascii=False, indent=4, separators=(',', ': ')), 'application/json') except ValueError as e: return False, DATA_ERROR % ('JSON', str(e)), "text/html" else: if not issubclass(self.__parser['class'], HtmlPreview): settings = { 'warning_stream': StringIO(), 'embed_stylesheet': True, 'tab_width': self.tab_width } if self.style: settings['stylesheet'] = self.style settings['stylesheet_path'] = [] kwargs = {'source': self.src, 'parser': self.parser_instance, 'writer': self.writer_instance, 'writer_name': 'html', 'settings_overrides': settings} if self.__writer['key'] == 'pep': kwargs['reader_name'] = 'pep' kwargs.pop('parser') # pep is allways rst html = publish_string(**kwargs).decode('utf-8') return True, html, 'text/html' else: if version_info.major == 2: html = self.src.decode("utf-8") else: html = self.src # output to file or html preview return False, html, 'text/html' except DataError as e: return False, DATA_ERROR % ('Data', e), 'text/html' except BaseException: exc_str = format_exc() return False, EXCEPTION_ERROR % exc_str, 'text/html' def do_render(self): state, html, mime_type = self.render_output() if state: a, b = len(self.src[:self.pos]), len(self.src[self.pos:]) position = (float(a)/(a+b)) if a or b else 0 html += SCROLL % position if html and self.__win.runing: file_name = self.file_name or get_home_dir() self.webview.load_bytes(Bytes(html.encode("utf-8")), mime_type, "UTF-8", "file://"+file_name) def render(self, src, file_name, pos=0): self.src = src self.pos = pos self.file_name = file_name idle_add(self.do_render) def print_page(self): po = PrintOperation.new(self.webview) po.connect("failed", self.on_print_failed) po.run_dialog(self.__win) def on_print_failed(self, po, error): # FIXME: if dialog is used, application will lock :-( log_default_handler("Application", LogLevelFlags.LEVEL_WARNING, error.message) formiko-1.3.0/formiko/preferences.py0000644000175000017500000001304513203117150017662 0ustar mcbigmcbig00000000000000from gi.repository.GLib import Variant from gi.repository import GObject, Gtk from sys import argv from os.path import commonprefix from formiko.renderer import PARSERS, WRITERS from formiko.widgets import ActionHelper PREFIX = commonprefix((argv[0], __file__)) class ActionableFileChooserButton(Gtk.FileChooserButton, Gtk.Actionable, ActionHelper): action_name = GObject.property(type=str) action_target = GObject.property(type=GObject.TYPE_VARIANT) def __init__(self, action_name=None, filename="", **kwargs): Gtk.FileChooserButton.__init__(self, title="Select custom stylesheet", **kwargs) self.add_filter_style() self.add_filter_all() if filename: self.set_filename(filename) if action_name: self.action_name = action_name def do_realize(self): Gtk.FileChooserButton.do_realize(self) action, go = self.get_action_owner() if go: self.set_filename(go.get_action_state(action).get_string()) def set_action_name(self, action_name): self.action_name = action_name if self.get_realized(): action, go = self.get_action_owner() if go: self.set_filename(go.get_action_state(action).get_string()) def get_action_name(self): return self.action_name def set_action_target_value(self, target_value): self.action_target = target_value def get_action_target_value(self): return self.action_target def add_filter_style(self): filter_txt = Gtk.FileFilter() filter_txt.set_name("Stylesheet file") filter_txt.add_mime_type("text/css") self.add_filter(filter_txt) def add_filter_all(self): filter_all = Gtk.FileFilter() filter_all.set_name("all files") filter_all.add_pattern("*") self.add_filter(filter_all) def do_file_set(self): self.action_target = Variant("s", self.get_filename() or '') action, go = self.get_action_owner() if go: go.activate_action(action, self.action_target) class Preferences(Gtk.Popover): def __init__(self, user_preferences): super(Preferences, self).__init__(border_width=20) vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0) self.add(vbox) self.vert_btn = Gtk.RadioButton( label="Vertical preview", action_name="win.change-preview", action_target=Variant('q', Gtk.Orientation.VERTICAL)) if user_preferences.preview == Gtk.Orientation.VERTICAL: self.vert_btn.set_active(True) vbox.pack_start(self.vert_btn, True, True, 0) self.hori_btn = Gtk.RadioButton( group=self.vert_btn, label="Horizontal preview", action_name="win.change-preview", action_target=Variant('q', Gtk.Orientation.HORIZONTAL)) if user_preferences.preview == Gtk.Orientation.HORIZONTAL: self.hori_btn.set_active(True) vbox.pack_start(self.hori_btn, True, True, 0) vbox.pack_start(Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL), True, True, 0) group = None for key, val in sorted(PARSERS.items()): enabled = val['class'] is not None item = Gtk.RadioButton( label=val['title'], group=group, sensitive=enabled, action_name=("win.change-parser" if enabled else None), action_target=Variant('s', key)) if user_preferences.parser == key: item.set_active(True) item.parser = key if group is None: group = item vbox.pack_start(item, True, True, 0) self.parser_group = group.get_group() vbox.pack_start(Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL), True, True, 0) group = None for key, val in sorted(WRITERS.items()): enabled = val['class'] is not None item = Gtk.RadioButton( label=val['title'], group=group, sensitive=enabled, action_name=("win.change-writer" if enabled else None), action_target=Variant('s', key)) if user_preferences.writer == key: item.set_active(True) item.writer = key if group is None: group = item vbox.pack_start(item, True, True, 0) self.writer_group = group.get_group() vbox.pack_start(Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL), True, True, 0) self.custom_btn = Gtk.CheckButton( label='Custom style', action_name="win.custom-style-toggle", action_target=Variant('b', True)) self.custom_btn.set_active(user_preferences.custom_style) self.custom_btn.connect("toggled", self.on_custom_style_toggle) vbox.pack_start(self.custom_btn, True, True, 0) self.style_btn = ActionableFileChooserButton( sensitive=user_preferences.custom_style, action_name="win.change-style") vbox.pack_start(self.style_btn, True, True, 0) vbox.show_all() # end def def set_parser(self, parser): for it in self.parser_group: if it.parser == parser: it.set_active(True) break def on_custom_style_toggle(self, widget): self.style_btn.set_sensitive(widget.get_active()) formiko-1.3.0/README.rst0000644000175000017500000000612313227323532015040 0ustar mcbigmcbig00000000000000Formiko ======= :author: Ondřej Tůma Formiko is reStructuredText and MarkDown editor and live previewer. It is written in Python with Gtk3, GtkSourceView and Webkit2. Use Docutils and recommonmark Common Mark parser. Features: --------- * GtkSourceView based editor with syntax highlighting * possible use Vim editor * vertical or horizontal window splitting * preview mode * periodic save file * json and html preview * spell check It support these parsers and writers: * Docutils reStructuredText parser - http://docutils.sourceforge.net * Common Mark parser - https://github.com/rtfd/recommonmark * Docutils HTML4, S5/HTML slide show and PEP HTML writer - http://docutils.sourceforge.net * Tiny HTML writer - https://github.com/ondratu/docutils-tinyhtmlwriter * Yet another HTML writer - https://github.com/masayuko/docutils-htmlwriter * HTML 5 writer - https://github.com/Kozea/docutils-html5-writer Vim support ~~~~~~~~~~~ Formiko have Vim editor support aka ``formiko-vim`` command. This run `Vim `_ editor in GtkSocket. At this moment, this socket work only on X11 backend, so this is not work on Wayland yet. There is bug for GTK+: Bug `721224 `_ - please add support for GtkSocket/GtkPlug in Wayland backend Requirements: ------------- * python 2.7 or 3 * GTK+3 * gobject-introspection * PyGObject * Webkit2 4.x * GtkSourceView 3.x * gir files for all Gtk libraries * GtkSpell3 recommended: ~~~~~~~~~~~~ * docutils - reStrucured support * recommonmark - for Common Mark support (MarkDown) * Pygments - syntax color in html output code blocks optionally: ~~~~~~~~~~~ **Python**: * docutils-tinyhtmlwriter * docutils-htmlwriter * docutils-html5-writer **System**: * vim-gtk or vim-gnome for ``formiko-vim`` Installation ------------ Debian ~~~~~~ Debian use sometimes versions in package names. Here are in Stretch version. If you use different version, your gtksource or webkit2 could have another version name. .. code:: sh # python3.5, gtk3, librsvg etc are in dependencies apt install python3-pip python3-gi python3-docutils gir1.2-gtksource-3.0 \ gir1.2-webkit2-4.0 gir1.2-gtkspell3-3.0 pip3 install formiko # optionaly apt install vim-gtk3 pip3 install docutils-tinyhtmlwriter recommonmark docutils-html5-writer Formiko is in Debian repository from Buster. So you can install it standard way. NetBSD ~~~~~~ There is not GtkSpell3 on NetBSD, which is need for next 1.3.x version. So you must use 1.2.x bug fix release. NetBSD use pkgsrc, so some binaries are stored in ``/usr/pkg/bin`` directory. Formiko call vim and gvim directly. If you want to use vim version with pkgsrc, you must fix ``VIM_PATH`` variable in ``formiko/vim.py`` file. .. code:: sh # python3.6 is in dependecies as like gtk3 pkgin install py36-pip py36-gobject3 py36-docutils gtksourceview3 \ librsvg webkit-gtk py36-pygments pip3.6 install formiko # optionaly pkgin install vim-gtk3 pip3.6 install docutils-tinyhtmlwriter recommonmark docutils-html5-writer formiko-1.3.0/AUTHORS0000644000175000017500000000015413227323532014417 0ustar mcbigmcbig00000000000000Code ---- Ondřej Tůma Graphics -------- Petr Šimčík formiko-1.3.0/icons/0000755000175000017500000000000013227326227014466 5ustar mcbigmcbig00000000000000formiko-1.3.0/icons/64x64/0000755000175000017500000000000013227326227015261 5ustar mcbigmcbig00000000000000formiko-1.3.0/icons/64x64/formiko.png0000644000175000017500000000627113160721436017440 0ustar mcbigmcbig00000000000000PNG  IHDR@@iqsBIT|d pIDATx}Lg~ǿ>싽/`^cFF M `ܔQȭi#'鋚JmiE骶݋E(iχᆠwIʉcB`Ysޞ<lll Eeuabe|*F4MSݢzo98y 41 㧔(Xlq ^5MӴL&sQo>`0m۶A (++^Sa`ddDb8ѣ.T*VU>l2 S~q%%=`ݣT! PJ133Q׋'ICU|TVV{zz/4nYYh?U%(Jn|Ν{^_oO+R:˗*|>_A?`ddidvoee{zz"رcx<.sul<Wt_r|~ˎh2 TTTbY1t]ǧ~T*e=%I|饗V>LQZZj@UUWv{KII ̙mX!&[YY)R={, H`ddaXmˍ,ۺe˖o,QJKs\og[WW2<555/z|+񵵵SSSײ٬PH6 wFIIj=/EEE(++gfff0444b(hvBrȩxZX'x^x<xK{;H 7vts\(// 0*fCCCTv80 æR)Hd[ɪb؝_ Y q=Gn< D"Pt9099D"X,FH$C噷zd\8U0  ڵk\6,C,[bKA2(GEEct:QTTH$4ƾo[;wdҝɬI122>va>뜺#N#H  ߷y(z:;j%?kŨ)JiDm[lYq + %```.]iί ÀafصkW8>eY6O.5ʃ%$Iz: JAUU8lڴ 'iӦQ6E,C,Ç~iv, EQ011}}NeQJܼi5Us=LJ]D,#JQQ?8OSSSӨl6[J6SJSw:p8`_Wuha*fff ـE_Hrܔy_|͙Ef5@}}}10HaF7R Br(Boc jZhll^TT?rEQ)[VXߟyAQv,S=whzz{$A47Vk}RJfv[6G=.N) Yw,Nq7I)8n4kP}D=>;;;*TUleJ|j @ٮi$#GZTҶP(hůO=$qa]I8fq]wa i?_; ;v L6q;r<Dbœttt\uݗŠiڨiiλ8a"qY__| `w !u(Jfy[2D:iUhi>gr!}sit&&& d2Y<#Gl `f&DoYI4662qHRe9|ż΍,˾\{ؼyD033_"oa1LiBe8F4p п؆Rׯ@/Ƣ( q H{J)4M,VonnѨ-eYH $Bj! #,3 ܳgϞ@4=/IRM&ajjjzϳDk}n;C)+ŏ$IP6m|-ٳڪPnmml6{:Ha]EիWquݻt:_:sih|\.w.okk?ͲapiRBȷCϬ/IRCBdYd2RJ?w9 8!di).B4"4eBd`Ou:+\Z7]?z(IRɲ3mmmߴZ|CCC#tO.( N*z-Nئ}cO>rn40,?z…kV;V@:fEQdYB 2 $vܹ߯s}FSSӡ\.>==]`ad2/.\XRt PюꝚrh[[ԭ(B^` ҒP4 % n ;!yHqn7dY~v^p,**UGGǻ35iB|R+[ zK<qmu"P.3nwu:X ׅ h ˮKG{flT( ?IENDB`formiko-1.3.0/icons/16x16/0000755000175000017500000000000013227326227015253 5ustar mcbigmcbig00000000000000formiko-1.3.0/icons/16x16/formiko.png0000644000175000017500000000115613160721436017427 0ustar mcbigmcbig00000000000000PNG  IHDRasBIT|d%IDAT8Œk`ǿ6-kҴd%6ѵBiaNݐ)  i$Vxܟ (HIe)n&ºNbvk5dccy?<}0??| \`nn$I0\9S988rJij ʍR J),Kz2|CCChmggx|midssfmqBŶ,zT(' 4'Ǘ^ORVmTv#Hd"ϿqYe~>::\ ~zr/2$I1@A6]a-1;::Zb~tވF)MB!___/E loopxxxh<qja,ǂ X__.c pN|J<EqBQzRTm~j16 ]ׯ3M2Ejmzzyuft]JӑyB/ p]yO0+xIENDB`formiko-1.3.0/icons/128x128/0000755000175000017500000000000013227326227015423 5ustar mcbigmcbig00000000000000formiko-1.3.0/icons/128x128/formiko.png0000644000175000017500000001603413160721436017600 0ustar mcbigmcbig00000000000000PNG  IHDR>asBIT|dIDATx{TՕ羫Q  ƀ#С(f1?!D+3L&o&2WPC3i5Gli>P@t7tU7UuM?oUkj=gߺ\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\>ڐ S֭['EQ(++M^l>L~4C?!` c,? 0}t<MӴD"q@Ӵ7o]GG`ݺu| RZ HP7lӧO}]S!J˦M^|lpL,'+?sMBŌ-0V{~` 8B!TUU܆ ӦM:(ޟ9+;Xiu]~ч?|\oB@ɇ0$h~TTUa7onA;d2R˗/xc ΝÙ3g`O_=Cr$Lu:PUǎCkk+jjjP]]=q󡦦e\ZZZJc-4|cR*0ݽ믿d85^7gEBV\a… tC!k -`6o,?~)IRJ_yѧ~}dϟ?b,+}χ3frܶdYƌ3z9 @kk+.^8ޡ}oٲb]?!T*UL&S@@i(vH+#@4Xq=zMMM@ kvn~4űc2g=nݺ;&uuu),zp$a~ʕ;+={Mxu,(**3gNɧ!*b9rdOŻ+תVtwwC4KUpH"AQٵ\pώ;̵LhѢP6++7|3***roR)477qRJWlݺurŊ+t3g(J.\ht-c `0 غu+8UUs370?>qOw}AAdC-G3l$IEKK}ʿTk׮)CD"ػw/{1{衇FLQD"axǿ-x|i*Z7k֬/glٲ݇u])0uT̝;s"(ͩ>#dY~b;! !GYoI~Յ>:thWt.]7?r294( ,@mm-chkkC8{߃ 6mFrMDkk+A?/ٴr3,8s 4MCeee}Z~&u-Y垞:'׋P(`06EQPSSo!9aO<1"<q,B}eeehmmB^/RvȑT8\x۽p]$ @ mEԩSYyy9-"ׇVXU_hX xΝ Xd $I… {,~TUUR 7:qc,[ [;.\KRSUU@4774M̝;cRyb1S%t]nll|qxCKK>o޼UU+˲>UUѣGL&8}ԩSbYiYYqyr\uUC`!ŋ`XEN:\ٳs΍bryDQȑ#8}4tl{{}&$I8PJ*!,X03J)Eqq1 "b= ͛w>#O%K)}8(# (s碬,vL(++CGGDQę3g:ZL2evLĉ'PZZ Am۶g}q pm+^UA׳#)8ǍؙσR ?f0 D"ߏ.nqLXTVVž.ʲ,KtM7OMgݫD"qiiyEQu]D".(8 ,:W0.݄B!477#L" jEV0tDc7A(7hL_d{si7h݁VX4?L&Ξ=XJ)$IFBPJ"i_ 9;}Y0׉F&nذEEEKR|4 5tӋ/x/ _oEY \[[[af !Ax<2B!aK5!j;u=0PK_4˲v)2Gu;=QJgg&"Xlܸ~Y̿u]:V^V{"ӳ0dYpDQɩ"ٍ_I;y?(J)J=7Z@ٔL&Ӄ/کӜڣc#F2 x 9Yd2pww7?e?~9QFlܸ?dY$ @aǙ8q1Džc vRW_}5]{ ?iZK2&[!_p7a? k&p(©3Vwu׍$gBO)=O(J4 vjƥصkWק?uϟ1V*vN<:.'t»bM],K? dd>aՏ|sGBܹsOݻsҥꆁ<(Xv=L|wݹlzb-Hٶ5a|~t1~t_ٳg7SV `ؐ88q?ΦL 9pi>Xxw|ny/..~lc2ÇsK䂢(CHDLH$g)/[,Bkkkef]]ݸydcr0;LJ1g6,O( T鑆;A^0e᭷&#tqZdY&x뭷6Xl`'d'1α(8h0aY?Q2Ȥ ;v y ϗn3W$I:?#l,˗/nd2GpɾƢ_F 4M(T{躎ӦMx4˲ŐL&|-۷u͚53T Ǚ+V7UUoP0dJ7w)I >Qޕ ?Þ&'۷RYWVV.EQ|:!c x7q;wn̰r<Gooo'򆆆^WBkBJnu}i3cS8 HT iV?=xLcQUUiӦwWuh4m !P}رcG]]?pu?$dc,\(^ -va͚5}aBpEE8qĘy?;~ %I*;w,!dW>cl:!dolll ,[%trH$2d?EQ.x[m6n3<G{D+Bȣ*Xt]g>yY[nx<Ֆei eqY=5k֬/o޼+MJbIMpEB?644{|}Ђcؘ-GR(%ag e]keiWqՔjXc,H)-QJK(^˲R4Mٲ,4Ͱgy/p8}뺎d2rxl6 ~4M[( =,˱]vEDǏ$I/[h4T*A+ü<ڵk3Ƃaf;)²vy2D*aPz*vtp8l0]׉(9޽{!ZZBvuaeR ]aƓ yێ} c{maǎիWRY~ L&*t]eYTN2esF:3ldݺuy9,"H L%?r!tUyyMӖSJq!I!Qmllko0W&TqzU{)H^?8K'+cUsTIiQ{ak W= v5I[|,, 0{]0;n4a u}@AEdqx<kTCCc?> 0 0~kce) o?=JdSi fGGTU,˵-O!p`(a+nM0>@ ;8чWXz9ik&W~R 4|l\i{Ase) <+f\rEdNq^x|e)$ `B |㈋ˇHnǥIENDB`formiko-1.3.0/icons/512x512/0000755000175000017500000000000013227326227015415 5ustar mcbigmcbig00000000000000formiko-1.3.0/icons/512x512/formiko.png0000644000175000017500000010031013160721436017561 0ustar mcbigmcbig00000000000000PNG  IHDRxsBIT|d IDATxy|[/{Hlkر!  @… I ev̝ۅ;,wz3% .Li]iS d-۱J]:#+lK:ZȖy>B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!wdw]fX,Y^/;&B7~499iyFUUm1X7 Pڊ @( |dYY!$_P@rn'tvEEZZZ bc!Q@-'t1ԄZ0: LD!2G>磬 ˗/h\k$_ G!yPh'Gcc#8kFH$~{aB#b>h4%"W~7fC:ϏfCKK xFP(םo !$(:ghnnFuuޡ&'BEx5MUU2`9S^^ CA4Aw((_?F!$,C=,qCCC%@tdB`>OW`)Ix$IhkkTx*5|G@?ҥK޽J9wLPG|p@iL: ˗gQUU|`dYFggW!O}S6YcO@r ---EOOh~BȂL~3c5MlzSjAe`я>f%ӯwLW @gg'p8Zu$(JѣdN./?;RqQ__w(%iV27Ac"䏢L|TU:K2LX|yA/F Bf+l\ߡw,1:ZޗdYV}>`0ϪT{Eq޷o_ p?*/׉ hmm->((>P(tBJKA'4s?@td,%Lz!(4qf ɠH$"|w) xTaWӴ@:X,hii$IzB(~P(tB2 f}WOcK,B >?;tI!,\/{FǿDAn;tc",L^'7|󑆆fYP)4 NPrA6}36Buvyy# wH%IEѐD"4Mˢֶ=uꔖAijkkֆ2C+hiiDy<vDyf _aX ˹oynw1bŊ8K.Emm-] M0<< ÑeM裏!k'O2d2a͚5 B8sL'cO۷*qB2+odYޖةz3H~%t?/]<,Eɂ-\t_|tzqE~XVw~ V\/Ancxxxv|ߺupWWWW&c#dGnzv/c4"0O6 ˖- "׋d~d8xۙx1BHmpW~{R"qXldhhCCCmBX^q`pQ U###hlla4҆Qeeehkk}%* nf3ƾ `6^y9p-}5DFjSn7v;E[=tпeM! ޑH$4 xq̙LwIn:444ɿ)>twwxչx#R:::0Yp|4qJbD1j455~  MLL\O^Me޽_3D\,Di,`ǎ6xGcc#ZZZ2;M066ϔ;t_$߿_ BDn \ee%IQ,g4M{:O?=cE'/-[NNN=o)@KK E(QHvSSSz1>tzB޽{wƟ P199 *++!Isk5M,ˮH$(|'CE%/p MZ[[ B1&_rX藮1Y7=i&k޽k2>q.էNoX`X </e?wg;|i_%hnnƲe l6hF1 ÁLτNLLOMVBrk߾}UHג$mt)6 lqےG`A4aǑ#GZً]EXssnzǡin7.^EQ`Z󾀮˗//dN @ww7nޡ$j2]]]/ɽ={|HW CS@`EA `q=G-k֬/ds;555xޫwQ(h4RiN'zzz ay ۶mc w=d2}0&'i`0҈c,qNiǗ/_~]ww E)]w3;DS---y;f%G'HvD"U<EQ//N!ٵ{$E~}*qTCY|eee-?O;D~]D"+!i z{{!L9m ٱk׮f | 2ޫi6*.X?ٌƔ@UU/ył?H˻M.d"zzz+/VZN%* ?|ܘLLL$|N9 "xΝ;{/yWcǎƮ;E˖-CssDQDKK /a^v]M|r1vU8Hfܹ3"IҜh!AoQʺٕ[lɛy_Պ%KU~`E&o[ MNNVK& ,_ i%6 ˖-ˋDCCCǎ~yGyށŻ۴Ǯp8 $Zݷm۶MNN~]QOAd'}(eGI+(FyM7ݴbddL0, A}}=4EPWWSDoPv=g1cu\ki¶k׮ƿ}NZjZx뭷'[399y4,I'.`@Ŷn1佽ׯz?T,WAbdd`FfW!611 .$?~/`mmmO0g)lD 8GGGaŊpxksB($ CҽQAeYʕ+/'}"wEn}}zǑ Pxq1LNNXKQ'v9gxJRc-w{+<;v6IR.eLZ@k0 7vu.E^Oھ}9ӹJ88,[ 6lիic`ۓ^q68p`P@śWΝKXϒl?M6U8x%!"QVVq`PapȑWO!)XVzBQ?D"E}F=<;QJD{TKJYӴڞ8uTq[o()|I71Lo?:XbED(bIx<4mʕ+W^p'L`ۃWm Tb*N'N>'N`jj %eK,hTW$Iw $}+V oOT8/_c:"Hʃc0$L&SV---lH =\jv-n9to'ݞ0`W^[7o<1j&] BqEƗw .;v866c ---T/P TUnJB[x̙3 GFԩSOꫯ{jjp8ֆ)Fq;ʲ Q[[w[=ib*x$Iz_EExG7M`[ytwwӐr 8s )|zRJvqY HzL2 2믿Deeej};LJB9qEq!+Xv]]nݏCC,diT/4MDww7-ꫮ;!}/K'r˗WȲCӴ dBSS*++q?'j:`Okkk? "x^tuu{_OH#Ut__,in;TS3!?6ZI/A!2L&Sܚ?uVj]wݿ;|Ccc#۱~zȲ)!tA-?xno=ٳgJw>La`ϩے=.ڢ]$l3*?cEu,)X?^j՞@ Ptx=fiGUWq\cWWWIn֯xC YcccW^=DnWUuι/ qe(،1ƭZwy7JQ/2%3Ȳg??#WFGGA>}:'d>ܭwd&UUNxl-}}UUU36>XOf=*ѣ D<ū(]2t̙3I;If0ٿZ kw{wTofbiX ^{si 7p8LWCSp\zRjNh%pB:?uꔮ˔o~{d2Y>fA$ӭ+~鴊 ];f͚}>_ޱ:H4bzA]]]?;Rwf.rҩEٜd7oOV㸫8d280ƠiĉEmpQ%r'c B>b x<(++Œ]ihh wUW]VWWY)e|^SSS gXw8?k֬p+sAQNTۺ&.R5=*WUU$(7oГCWWl@(™3gp8hmiݷo1$EQ|,kA-Ç8n3f {AhulRt#3aÆs^#tΎ@ >;v v mmm?so֭byy5d,ǿ3Ǐe㛯gN566>!IL B!Hi wׯ_n;bWB?1gY -&I:w fÆ o0}N|OngϞ=f͚`| G^F:۷u:X,Y/=4|>v;A*6s;{_H)ٹsC9>}}w_oƗ\yu,? ;rHQT/ot\>Ϝ$vZBpҢi088HWcCNk.h4X0PWW+$Hgg'٩()OছnvppE9P__ /@?;tQ|a|xu?}}} 7 ˇ䒢;aÆEXJ] LLL4_Vmݺށe˖}SNJ&KMI$5kZ/`6u/iqEZWn޺u둮A)fEql DR"SQ7x055嘜;@ǿt-ַv<_(Jq՚Yc61Ƭ83_ Ǚ4M3pg pgOMMM }?'I˕pc 555_{5{#TvZ>88x6 40^`ݺu0 h]Ay^f1e`YsRU}}}}!M>裏>7xl6{_x1a@/%3^~AzYK ͆UVb`0( %q,'ߏr;lrdzEx$\9c6O jp$m%@__ߩ LKl'0559xp{a0|r444 ^WlAooowcCW]uՉbۯ; ̹O j~wppz5䉒m۶3.kqo@odd$ё\`MMM9oC `#"\H4vp8>1wY[nblllҜTK^  hDKK Vkg)$ IDAT# :|ʉ뮻ZQQ1/r8pqG[v LM XbN'h"q.]```Ns!O08x2W.ڵ1Ѹ/}6˲Z]oM摒s=xv Yp0zF2d2aժUܹs[[e\dXEQN)k77Ɏ2cɒ%0LzC8466NCCCrQ?c춃˵Dqf[od=@6J.&A1rTWWV5oV+ZZZtřG|MuС32L[i122pjbP-PUU*# `ҥ D6w4m磏>F n {ݗ&秒mIPPU>F@$N***rJ]FhdYF___&c7oǏe2eŊ 0gkuUUS|P8bGjkkҎ$"atyǃ^D"\8:t2ovRFn˫6V+َ% l۶Zw$?0`XP]] ͖W[Ebhnn 9qܹ)IR5Mùs6ڪ~/ܠT 7x.^wTHE+ R3hii<,'dYn.7>#IٳnA>Q6i8qAe(yꩧ[ܹS$i9#f;okMzA qXd sk_v{ 櫟8p[o%II_DUULNNNleZaX߯)<%wraxx8i?h~[^t zB l)łH Ál/˔ x?W Ԕ0 L:a4QYY7뛮mQU,ǭuL8R^^~7ܚ#=vzTz$Iy}@(Ǚ3gK/^Cww7\.Wy477c͚5Ξ=B9TU7w_c>`0@[jILLL$:OE# a|||Ʈn8s`08~~SP#4v N|?,sHnEA_***j*all ===>H5qoxo0 7EEAYY|8B8Nz>؇\pAחpdJȲ<]K 2>1}}}}H<]v8;RL&TA8W/H}}}Z7_'CНf˲&xޔ{0=쳏 ξbbV5ei"BP CTy%l߾\wtp7Ztl6,[L %\V0gVQB+*xU]]gx:ish~ZCFGG+Du`6!ϗ QUD;G ZZZt_V\.wvcdds0jjjnp::TUU5c>=]3044ǃƤhDcc#<ϜzTv=q_{TUKJ,˘pݐeFQ+\Rב ߏː4EQt:6K øx|>&&& 2$Iqmg/xD"ӉSl`8(IA$X,x^J\~;RڢE\Ѯv`h4fut@$\BD t''S>\.D"ף,R|@ ǃ9kǃP(40QaZi4MæMQI"]s5E8yTTTB/]Tp8 w\.?cw`,ٌ;v$~v=nobr fx< G3O8/sfKk_p8,ƪg}8j fn?nݺ:׻MXYYx022颰뾮FFF[Cbbb~H$[UU'æM4Emm'Jtw"y. ժU/\pbށ ۾};NsqX۷ou] Pv=eZ! B`“PjXOtԩS ϐh(P_7,:@hdwax. A?LIv̰Çvx<Cccts(^_eϘoW%[ӴKcX~}F 4EQD{{;֮]TۛrehPU>g?;4%7pw8O;Bq$IXb֮]ִNӓs!q B ,GGGSzhnnի3gl\>'OL@h4bҥIk.w"t9rdi% ####;t^! :Mv# ͸=0Doooҟd5\pxxw0>>1Z$}\(R$IϾIZ(.jD hsrME`͚5XzuFCz{{Ӿ,DPsD֍W4vÖ-[PYYKuq8`3& qG8BV+,;t% 0+@ Dz,Ϛ=򠮪*\.Ο?cǎahhrA#N.\(!h!p8prv'}f,]tEQ^w:Y,iden1L&0!"`6q\ǿB栓Ruww{7lp~X5I0 P'^2Q^^rd@e,p\qGU8`/Ʉ7f3 @lS]]&0099MMMX~:c[fk'O'!QzzBH2FqWdǎCoo/0*** o{<?~?&&&6$ 6 N3ax;baƍ9ٓ8WyyR?͆mS1 l(b>q' P#?]jG@ޱbXa^tuua``H555JͮE %7ǡV56p\Iߧ)CQ1D"AD"L9 F466"l0ęh~뭷w'IPC[l~0p8oCuB$)e|Ewށ  O|. abb"J*MG7J6.I6mڔMcyA4TTT}_P[[ ٜph4bɒ%~_[7qa۶m]]]og(Jrܹsu߯* y' @p8!O%+gAUU%OVXOm/x$I)ł%K`rrrwaU$1py۶mǓxDC6lx<[L&Sw4 N}JV{ח`ll,555iqAg hll(hhh@ Mf<EA۟9v3Jtڵk|+XHaX`6s򞙖oUTtv3L9τ*TUUr:E$7`47o~[)3 @'VU0!0z-;wFM$ΩΏn=22~ݺus򏪨W_bHVo?VMߟBІE$!̙ꈝ >[xs1 TU 57w%$!!3I[\:FȭD"~KWN3gKW?[8d!Vti quܲuV녜[㯫qX☪yޚ*k$)ڄd @s.=5UT?`# c0EpxzDh4d2%JnꪫiE zЪU~yi>u~^} ɴtvY+++s>-/TC/^L7_,CɄp8 ٜe [li (oU~igyD`) )//ԔaQQQALNNƽꯨ=<,p8 GN'FFF͛QUU~q{8ށ:JoJӴA2󡯯/S/GȼDW#AvZg=~+**敀Up===I/]k׮MJq8pw `6l07 $`01VԻe02P6Qd|XoV@u ޫoholٲ &`Z!|>_ƲuUU_1M K)\ "KU8TB\G%eIlٲyojy~wG<.:سgO`Qc2Ӊ@ =Oݳ$IieH,ĕ)10p:pxSe 4M`jhh@MMMFb!*ŵB_|ݻ`0{nbb,C$9gi&튊-tbifyyW@J1%zeQe4jժBfh8Ϫw 8Ⳁ1kvϸZ. jy7 =~wxϾfSUU ٜx’bL!JLlxO0L|֬Y0FhAx#~e&>8n|xP)IRN[ 4?ÇŋYnEQo TĊM+lBТUahh(}}=jkkI}UUU>w 4tttFt ,//xvkSDQ^z/Xpٳgqn˖-7<_85jFHH1n90o1Ngҡ˕=F#֬YHzTUA%i$ӌG0nw $IB0X|(# |_|,/~z_5\ye4M7B;$ F& (" l8O1&5Ȳ ϗ0D"M|֮]KC9kǥ%%'y,˘HY]lZ30`0zDQo۟eEp .M<7o^/,7׻JUO 4܉c'迣DzZip8iM;c3i9IGʡϩ|zo(HE=ՃTUJk(`iF[ԧ7|ʕ+6N'@ Pw|]wS#sNgr>rҥhkkC0H´bK]/v9nt aN7 CU (>Oqd4 .k^|+** ߘL920ȁ . NAuu5|>Ο y@qJy~ FQ\L$6 gdٛo9b+h4D"w|6UUݪ|)"***xz:88Τ%)| <`k)2g1UUaZi?lAf&O}ci4<NLw # ?8q\=dZdGfJ+"=ژi]f\x}S8DcAl@>яn4L?E.ZIGfpƍL{W^)q V[[/ynTVˤiEUzbD~.UUg\OMM]+Iˋ~JqG ,G  FBu3vjjGefx;%IJ;d [Uihgm}!/3NMMM-A@^ YGBМ;jl-1i;::~]s{8OP=p8g'4MC=-I9<ϣNsQ#'I4{yǞ[/BʓhD}}=^zh%)6L(///ѣ"al$_Pp w=|h4eq&i ?sρ֝ܞ={:Fg\( |>ߜ]tm6@ҝ-rk{+A@EEEZ.ಒO𖲲X}Η5Ms=w8Qtٳd2pHSSSÈD"xsQ|N ߺ:Wf (x<ӯ-+#r!3zǑ/J60'L&&#NT1U4NӴb}ïe+^ZggjEQx `# W3Ʀ IPa:["'b0k&***hi_B%-޽{%IWQ˒=%miaUUϿ ɼ$IRZ-TU;wbM70dɒW0%œюŮlH %1H7˽+s=?X,?Iu.K1C:={|]S=.qXܓ?P#@u'dY^ /,Q6`, xJя~ tU4J偏}c7q~' 80 o!?ӱSZM6mxIUe4Hݻ6jQ%} Uuk{tK/۵^sddެ!wDQK+vi0L{tb駟^م,JGGIgL&mPG ߼۷*ϧ_JQ,˵PL8N{#lOT4={P+VUh$?JyWOڢ3Ly @oM=Tf˗)L]GFFEDGH=M>n&H)qJR(NaUV土u{w;OJu{OZ=cn,tr``^}Ut Nضʕ+;z2Bs !<)0{i^49s@ p)tA- a7l2N8O>5j x-^UJŔqΓ<,^3nV9 ]u> 0ann[h+cl|RᜧcikP ] &m]ƆΜ9!JNF"pGG_?#zTMS%iK*l~o__p dYsh4zݶ)ſv޽\]=,YWJyCggD"AR8qh͒L&:::TMJ4}``f"ʕ+_?~/0[+mJ/ux`64)0kK}>3 z{{K&e.'4 [57!9teW27ǹ5.qѣ[oy} h"йs1F1✓eY7̟?VDǟv|'J)眧o`t:Jlۦa?2Chfnr'x&'Nyhph.^H>neϟLsy@IPclĿBI@fҟJ{2QB-8h=ٶM.]I;UQk::u]'0f -j?(MysDS p['6/7'AAR&mri5M0$z' (p@0D"r˲Ϗ; @<:V^zzϞ=?L]ϕDQ/ ۡ-[*/B%X2Co)_xc!,^\r=|%b,͟?u>R!'A)EgӧOS?E"J$*1Lk"K,Rw^1^|2xXlOOQ!DgyP %i[e:::F/e Kny+sο$(hK9%5mիW'=#]adOJ)R*=NOԧiCf0#")JH$20(hjsN1eYɓ'#k֬c5Msnܹidtny/DD~?r Ln"Rn+BP2(׾WxfW>|>aPKRd}˲7|3|cϞ=MTܖݬ-ΥZBBBTgn?N'j&cU7 x:t(o C,S`@ 0xwW8h@e0$(\tZy1F>﫞skn{w2 p8l !^Bбc7߬[A|2zzeY<XBd5mv?z9wv/qA~fgrC&"q۶+^{Ξ=[ ʴ|>׷|򙕌ښ6P0E-/4O'?^]]{TkE[w !jڸ̙3hdo х DcWlٲٕ2vM)? r[R㏺9.J)_H }""Zlu@40ĉ."P{iNFsO|>6<<|i]Z2P)&sg׮]{ S)}>ȁy BJYrn"oz݉aPU2ӹs8*is/v5UOM-۶qPj- +? oolSMuupp> K&tY[kmYl;*_4]P*? ZhQ? @OB {B0 #cǎY_ߠaeÇ= mAV2lۦS,S~k-[r㫖K9^lvR<W.Z5{>ǹq^9dmvCȎtaw(P@q:H$-^ܗZj@;G<y-X"oa|]IP7K.ˏpxΝIg?K.;ȑK)%]rS 4mGϟA1VCS%՜7xI)]m[efXD̞* x&OmlRJ:y$5RU |>c>*imHaYAU[IPWR7y=)H 9s^{54 j 7xC׃#)ޅ ~\5jh஻z*RpR29n/^1iڤjS#'DDo&?~S<haiDDoJOI*k&@;!)4)0=Pzgƛpx{)eGGp*@xPiaOz APЊ+kjỂ;ҋ5)%ӫJ .`@̘1B{{cg4 \h֬Y57|Gi1Md*N4S{N9(H$C7yޱcG 6X,FNݻwɓ'PСCie5i4 wMJR9n:;;1FBoʳOa̺ t:<_ԍax;jf`6]tO^48pߦOr!p !HJId~?tMt 6y#NTRJs /;M\, qxwO]&344DǏ={xZEw}]DD~{{rJFFFŋJo۩=qBX4tROJcsUsa#G(/^|lVgɶuŎqoJ&tڽ{7?~Ҕ |o|}%\YIuttPkR$Z swnRJ eY<u#c=|~BE]O CAt!:p]tIL/8tGGǝ{?u֬Y KV5Ѱ??R R- xS`P7tЖźJLӤ9sLxXB&N&Ts۶m'& LJnUcΝ;;qISG=?>#k׮{m<| (:a1FsΥ$ʧI7dZ0~A9((hK-rUI$^f{S`POnGc,ҩSh׮]tI !Dرc+LiӦaΝdYPHv-SC&*1mk^cTt]w:wuS NիWixx8Ӵ'lٲUkiiiWtO7xc*4 {92 jr4PJ߶m/5ƴaqiӦQWWM>=o3J)Is_usbh!ӧi>q (ctzCNL6ϗܹsK/4 xÏPum#/LqdR#9xÞB\$w͙3R]z2㤔%O0\BW\Vٳg5)%qD"I$w[o}i M7i yڶ}#Mf !f7d9OK)Rtm~_y5Ź.rt-0M:;;ɶmW1&nG+-F_N>M4w\|lTtt={SK,]"zn)u|"х-[D"x|6mB9!D !fQGD~)eEJR18!Uu29sfDJ9cOg+Qi#TN xVTz IDATS`('}Cu={6qd7-pn46lp5a@Z$;wΟ?Of͢9sPGGXD\oBSG.Z6]׷ئB%*X|{5-]8?sJ2B>̙CX&$RKcdRbJ]EKKzOACR~tl&N>t$q/޻[Pz{{Z[[iΜ9I\bFU{ (O$ݺuk]wmo2>$% 㧈(HDd^5$@T@ @sΥ`08\0۷oTH{՜"|l'xuH:tM4ͳ~pxR=eZ޽N:հ-SWqm{0SŎD"[o`*|4M/o!)kk+͝;~?9{>JD' 8N {P־̙C~Z[[,"4IcA_gTdRi7\by 8y1ɤ;eYn޼Yrk׮wv͞={aH,޽{S 3רX>iey˶DtZa=#9AeQ `0HmmmN9 {Iujm?GJIBr'3^z*^V=zT=4$)D(oFCaŋgG"KtCL%ΝٳgV~lkkWʍi֭㎏ !v;Tk$1F_5M#Dz.!)eѓ**|(眤⋟x1D) qDُ !J =F-[\!-Z6::WޛH$J68]t.]DEfͪxOL3a\Ѵi,˺{?PxiӦ Yfi؍jf {(@A1r i6~aLh֑ϗdS);T㜋7ޡ pη>YUWC_V#[Fs_```M<`unhhUFGGcYѽׯDP(=?cg] FuԆ,(t;aYr$<؜GQpZu*7bP㏏ڶ=Y wRP(۹sfΜO---F2=CǏ78sE_w )"Zy2k7KD?o޼;00g=lۦH$Rvhlm/u],]XvMDk֬ߚ1 5M'%C r ID!<^y߿8^ZjFyqnٲhu }>_QJINrs.g̘Ѳo>wa,_|eYgYeUr"d(z.A>*TI]]]VOD `0wsڵkn;l9_L&-jYmBs(MD6%5MRF(iڠ"cL(Cc@DyK&[u͚53- bJ~p:LoFsNTt]ϝrB#u q;v<@D̛7Z$)pE>}:uuu̙3ɶmP::s|g"6n8DDqi9y;&I)S`|xvgg7nxLJIt1ڿ? WeGG7GFFn~衇_O Y`җ͛7eImoX,F}}}dƄ**sʇ bŊee~Hni sݻ&͛w[4oV hY94mڴ+aC=5b6[U0n/"M7DsTpXd g͚5oO1Kޥl̹H>|s3gs *˲,9s3---dZ,CdӡPRhZ[[1k A#Ikzw4Cw͛ws"xp``C(OCa^EZZZYn)!"4Pcl /0Z K[3ga1{3e-H !Q:V^ig~υd/jp}ĺڶt?G3PQkkk0֭\LD" i6mzvQ> Dc{ff%sQح===VXeݖ{.&6?/h… ; .m`0~z'rn mݶpڴi_1 8cgmb# $i'7mtvC @Vl"TOOf6@ B3K%P߶me"}7~``T*UVn70a֭[u @wwDŽE@`q2|>_7mR\Ecݫ1=== YlvhbWj.zz !W,`QKK_ZaLPPom[mR񝍈hR ؄d'?nP8+.vzU.*߿0BwoTJ2h\ww344*uP9i~b LT%Xt釅[ӄyǀ^JImS2d2===W=M t3~r-M(h\P[VZkYև&BL # ň菮G .p&q&Li'6X]*hqo۶q#Nkc|?臨Gׯ_IU,ȾQɍ .9t۶}k !(xVwq?(L#V\!W4O&:qΧlk۶mD4@Dzy~Vqf1:783KQ Ѕ'(fhFmmm~럯cXSBY >y!" gBR)JR& ­[-wFFfMDDgu]_sa&r64_*uvm³|U| ҔRBdH˲-dYֽw4)J9ȾO$!(S<t:M17? O?F(4P?[sV'*]R쥠  V!&$Bv";Sa !M6-g֭[%f[+LR,D"1N?PGGC1!͛7қA-LJJDU n|-uRЦlt=+7'>9::eٶ)Q1KJ9:mBi!i}P 'ݿ8΋^/tzn8`"d2I>ɱ~"ZyfO`P]Z5DP(AUXbeY_ps?*כg}2)uۻwoMJ뮛q)EqŊF'$R'C=DIb JJ9)(r_ 9WJ0ƚnbT0!X|d2ʸ~ܮ JQ<'?j'n݊0 q n ?ڼy?,0Y16m<.w~ժUO[i0hpp4b1be} Ðtm۷a P7 @<7q5eS FFFhxxڨm1n&I))Je&z5bt"իW Iټs1ƈs. øg< cl$<'B;ls(E"4c "JM,ҶmJ~mODD3I|^Ռ1rg|?kt ͎;9O6 ?~ !8{gu?Hl&Us8J… lg.%E.@l)E1v"Zar^)ekkk+1#!$"Gai54::Jmlj: POS*H$Bj}jcH`lt:MDlۦW5ZuL%"QB-]+|16q 2][k6ih*m6 ]Pc%+nyug'˦iinJwc/2v?9ڎ;C,]_MH:dP(tu"Q f^gQ{{;,kFaDt]Νk'TH ͽLlؔ1MSt1 N---42_9dFs޳sO$B([wwmfU9H$/V~! / )1` ѥBD7LXK)[c-`ПL&[8Q;wRrU B [q!Nc$cΉ9uc#rݐg\XiÌ/}`` ø۶q߾}u` BPHƍOiϛ7/;}W+DT`> p j1*\RV%] 媷 CuCu9T>xC@y  "t>C<p*C @] WVcJ+H zJ/+B<1|J-@J @ \'vC0\o:̛7;p16p[O W8QǪ&T* bv4p0 2D"Wʂ\m;X aHj 9XpeUVpKJ)]0C!Ƈ*C TƇ*NucWP$Z/ @!SH)Q1$Pq"1$P*Q=$F@Dj &P1RR&PpsU#Gmmm4s]"k:rT*HRd6 !HӴI>JD4 IieY9"^(|m 4MӞ>}mۦX,FDlۦ@ @DDiHlܺuk_Uאg?@2<Lh H,m# go喗SO=8Τ|%RG֮]+jʖN_rq9AՃW@ْ_lٴiӅE!=3'l~+<{<\$Pts*W:;;4 ( T?H)EŸ<~@m!BT* " ?R ԰r?|"rg1vRJyjƍL4i+V^4Mwz{{3 B!(a;5MӉD7 eժU|eIe˖>*( xzYJ&L&jPhm۶]ITP$P+"I 4MgH@ӴV= $PТE>iO:20$@A`b:r;w|q@@A6lMK)&QIc{lj͘1mƌFihh Ø0 h"H() ] 4M#]x`mR=o0E !&3϶5D4ychXH c9 $?ܲe˕FeCEI)w*UBk Nw˹?aY_>"$P3N&&4M[aÆx}r!x ˛6m:^ Jr'm۶y-7o^W׀l@IdA"=%׏@ce)OIDAT?wf&,IENDB`formiko-1.3.0/icons/22x22/0000755000175000017500000000000013227326227015245 5ustar mcbigmcbig00000000000000formiko-1.3.0/icons/22x22/formiko.png0000644000175000017500000000155213160721436017421 0ustar mcbigmcbig00000000000000PNG  IHDRĴl;sBIT|d!IDAT8TK[W{ύ5I0f,`6bԂ7.L=lAa+#PdZBh!%Yb6?3^oLƛ{blmy9||>r8XhIe355us=566uttPf麾Kxܪ8 zn> 0nƍD"Q6h?77RN׿֐Jp8jLUU*54>n{{;iwPQȲ0~x<twwrAӴ3Azu?LOOȲL&vn@ZQkfEN'n7:;;߲GEA&RZuݳX}}}rPnfM1($ "~s. d\E,k?(bYV1Ekkkl6ˋ"c $baJ晙gotgggSd2^آ(1c P(}>]kgGFFn>' UUեQT*}[L[z~~> eY!\?%̠bq |T 8>>N% =V(^qVQs\R#¢nF$n7%Iyf=*:"H" ^'&&Kh40hNp/s544vrr+imYփhB$yU/RǗ³U+MIENDB`formiko-1.3.0/icons/256x256/0000755000175000017500000000000013227326227015427 5ustar mcbigmcbig00000000000000formiko-1.3.0/icons/256x256/formiko.png0000644000175000017500000003623513160721436017611 0ustar mcbigmcbig00000000000000PNG  IHDR\rfsBIT|d IDATxy՝9ZUUw 4 /▸DyiŐ01"ĨLf2f&cI&f_56IbȍJ 4.DY*^_/^T?Tou0 `0 `0 `0 `0 `0 `0 `0 `0 `0 `0 `0 `0 `0 `0 `0 `0 `0 `0 `0 `0 `0 `03mf[lዊ !GQQ!'CP{uc0bĉsNuJ.ٖgBPWW b6j({oԒk ܹf2l6̟?,>V0 E{2f,9vyRzYe,UUUq; zp]vqyM; ٖeȲs碰0ۢHPv^a>YS;vXH)T)))A}}{قRP(UڵTeb&x_+DQ>o qFO)f& 2k{Bu@ cy-< U7|U555W+|L#"'?SPpo;kɶL s0 Μ9s#v=/WΒ,X6-ۢd˅?`jVĉ,Cqbr:F>)PWWht]Gww7:;;A)/ĉg%< ^z$Kٳgc94d6? hooG Hwˠ*e˖)P(epOT̙3'Vت*PJqytvvB ~t޸g-12aKrCCc``{4 N nguI S9EA{{;ǚkٔ'N2[6F0L7\[/ti8<JKKqjM~ǃɾ.'N V޲}+VxpٲeSNe[`̻':ł9s栶 3gJJJL'W==wxC=t([:AxZfi麾WU՟wʶa،4mJA?PgΜo;90ÁK~j'(‡~8u<<4[ou,},_F)n7n7xeY^aZx׮]|ͷg[tG+N/DKh58555{+QGfcc/||a۶mZ,s(S/MӠ(W^ylɜի]ǰ9Պi)Պy84 Νo8_򗟚d~{ oKT 8Rϟ?Z?H5@ H$ׇB0_|L˞öHwM`0>o&5 K,N~ߏ?Ь72@N\, zzzp9{b)m֭,@YYCFW<@'#]t>{ۋӧO#="M4{bIܔ盚=|>t$ (~{ڵ9qi g>srhh#s,7o*++&x.pmmmS9ޛw~*f*(L ꫛ3"͛+Wb3!H) 'ēI(!䋏>shgΜTpGkkkmիW}u߫UUU(p8|5$`.YgfaXbEE"e[t8\x.ۂڵk#2x\y^H$p6-"p8PYYH$O<3˭Vk@x'؈#G$Do N|A.O~`$-[̘/LDرc;YxTQ躎RB #N>_bq׻R6x(Bgg'_@)Eww7L˕0zժ8qbFt1^`OL'cwww͛RU]׉((l .|=.Y6{ 8~8 IJJJr_ re[rի777Uzl*.\x_tpGʾ># ,#-VIicIAƾgWgjbZcYfeM/VTtc)vɲ|?0rf'm6?f7h۹/`js-^jݞ`0'O'Dcc#>q(hiiIU3ߨu0 m //td\6x}`]s(8 ZZZ@)EiiǃgώW3 \|饗:3vm$]tQlrL~?gΜZj㪪^.놖ňknaǹsueyêU /: y# l 2IHCC.#un[ZZbOeLY1ͮnFb z@ʲ|ك>ɶ eڵdY0r}̠ؖ/?t:ggk|)..ҥKbŊI3ZO1:k`'HBDb# !8NB~}BvY#'F}p8^8yLb)kg+BPG0y].x 曨ʕ+tRHRj_zr Պsf-`2 o;vw9&B)>NL`6Yp k^4-o_֖P2!6e톉t:1:XyaJ;0qq>uwwla66 -Byyy%Baa!aX& 8SJ=w=-!aڵ?eӱL93񡡡˲-G(,,DUUg\AP[[)4JvS]S:`$6퓀Fñɓ7y%Ky:U!G믿?V5(**ҥKngΜֈ'dPUΝK(=M"|͛PVVv fEQt:i FRJH$188ثiE$U#XΞ=d&TpW ϶LuP^^Q36vTs"<:::8 SJ{ Ey6 RxS^VEEE $AQpc-unj<[[Z[[gLD2žFc0gΜت9Y N|!HdsOOK,p8ٳgrU8;|bA,u](Y)3mf~DTepYf%,w줂 ${$`0Xr( 9{,PQQ<0g`:dikkkY|x>mY ]188sappPAIYX`*%?||D"$*kOO\.D`PYYYBR^x^lSSvHAZ1CdU… 7l˒D"\.&e98ա~(hkkC uQ$AmmmB$Ihmmy5M5`X IRu]wɓfdVpM7=t:&L>D:Bggg:B.){fi, ?/VUUa޼y vv\ >@  Dt3gμf2yFO#eeehhh@M .]ӓ&Pà| DQW_=nBGGZ[[! ͺCUՏ^z饌6Pf͚v !s7o֯_?e?IL6#V\rBcG%}ՊڄP( j2NެK,Y^"ñD B)92v; _ª*̝;wB+/ryX轜`{KK}̑7@/t !)..N=qh",^8m֢(hooǓJ_@ fSJL4≘ At:qytww'%i @UUTVVO>d" $o,(˗/}([UUs+0>ϱX,1g yֆǏ>v=\.={6c&CMe@ Oe˦6vQ^^2(󡾾[pNj-sS---+V|뺞wL2V󡳳'ON3cMBN'TUq1%Mܹs u* o@)Mp*bĎ )+}_i3G)hkkk]tzYAHp 6ѢZEx'Boԉ8%%%9W7/_08RVB ~q5M(ɘ5k,Yb}p6 cx? t޵{%EFʫ,bn{ž $Ib8ʢ G~?f[I>())틢xB ƒ$K.rLAp饗onn>{3GNA3uUUt:t2Z@lӧYf]QYYXTTr`f$_<~P(fz 1 Zk f"4!s^.z8Iz, SeȲ) «ࠩcʲS`0qnJz1OAAjjjr|f8ؼys'5~$!{5ä~/lcSJ5nC49:9?K\l6[:&u:YbầOFYYY>EQRp$썍Yo2u `۶m;EQ|0WBN)x<,Oyٔwlݺ&?0APDMԉDLh+6d4/HIDATw:nw,L8SJȩKsɚ뮻JEyb|6z-Rꡔ>iC\iپ},?zB4-#3,z۔2kQ B'(Ɨٚ:? `֭ g%&Uf߾}?ʆFq]wRJ_"\%pSE0LO5ѣr`8%|'WYf5v9&qu=c}rBHN83~$oNs{}#3m۶dYZ(}>߸+ SrRͦV]wuׇᓑH0;uTGc@"PDl EIyO)=λۮ׿>hZo8wݼ뽠yoT@h0 ɲ<(rbybZHYf-;:t5}L qɷ,2Y1=PYY'-[,bE|D{θ9yW !AB% s7ukv_p=tи>|᫯z]- gPB$IZX6ȘƊkI{=)ٌ[ojQX9֍N9K)0~I"Nꫯf7|饗龗 [!M}La$`+ 6l8. eE Aτ\FpwXt], 沮:Q!?ݻweCi@˯ P(Z8H$jmmE$… gyBN@F|ҟup8@ ӉP(_kl۶KǝX, R ˕4Lh/@YYS1) I.s om߾~-2rtAJ7y>|ر)4dݺu9s+IelSS֭[9Ґ-&͖] ñD'BdYF__wxx8󫨨V#E19^7u +Qz$2Qn{RSbEQ$IEgRL.DD!yw"( N'8߄㺮D"x'd6Xv-..7t">#A)8ҙ6r|Hek63],zAx*gJiRGu 8vs6(ZĉtہrX,R:C@*_ӴtA :!"]O*W$̈`F)pG}3z裏o%bF8qJJJl6*++|.)7Z0L3ӭ$:pML~'l!$iL Lxws1 m޼o?TZZwQ% =NՒa " 7,LEƋ$ %_,,,L0)R +~뭷ٙ큪q ;oYc;~/֮].Ŀ>{aÆ;JUUgB(eJi1RP@)t dB,ٳ'sNoZ"yp'~b@K-v^z]SG(--Emm-*** W-?hRZ_~yߦMn@̏ !}ӦM뛔F:CCAA4M/k _GL(79iZ[ y(**j5k%^ʇ Ǜos}( vM jjj:L) G(5$MZSe-88twwjZB]׽>9 3iNˀM>zw755t^tEu? =9H0TVV;u=f8Xq-J^,,ǜ8<'00( 8[}r޽{s &tbɋB)mPXE㦦bΣ۾Yf-gf t]G__`Q[[ vtNg#rpc]w޽{_ټy***^^GZImY<HPo)[{ڨ1DQo>No^4L]O _J_MMMl~|0/BDc 96`>88UZZ:o"?ʳ>SUqGA>3_kd2P,2 &h=Bݛ7޳l{{{iAHlbOᄏJ'ѝ"kL[`Ĥ y}${Re ݎ2~ VH?9۶ms[VGU4/̀ Ye6ᄏWU(^WBK'# Ey>nZq\ף(J8P2aAZ]!Dҕx(N~|СgAVظqC)up;wBui.2 P/򡄙BbyD"\>BHr#oΟ??'OLzq??t?fC@شiӏ4M+EVH$B"TU_dO̒8M+cH$ҍpNᐜN'/[G8l h$6lWJ N]1<<< ꧔9fLL ߖlܸ񇚦}R46wRp8`0;SEQӟԛqd,o o +r-?VU!bQJ}f|O*8K H$ fؾ}S|m"1_nʛ>B8ocfغuC,/r%y^Eς 4M031 @GBЫ^q\(%vrMӎl0#s>-gϞ OH(Id20!$rM: mc$zR`0We$_ BjA` D-!$ISy S$DQ`( a )SY8̃H5XfM^f$#H$H ˲)F(lםN(b5U i0Hb޽)S`$w?BS`Lf<`Lq-`6L0&85 Oa m 8ly0c0MGpY!2$I+,,|>2 Sl)**:XP(B2널G٣}#FNöR%u)@Y2/H`D['SXc L0婧zJUToエ`ljj({1)u)1@)TM@ u=i NVOٖa `\A pac`˶mX,PhϦ< ca n:Yw@TB*P'J))((Ď0` ]K^/<"H4%:r1)FJ6nXKFcn7,gU00#%ҟGG"uA^ˊ@ S` ]oFTU]8?&p`R 5#-[B8 g?fL0?8{#[21́)F1xx:+1L)FYH5`M)q~С*p`$q`$w a5L0X~wRA8-#J \Vb S!GH9g L0(++{bĢx,˃GdY4 d F%%%l6` q-H@US'?!=Ѭ 0  X,,?ͮD 3!i㮻 !ۓmy|$|>߭&6 `0 `0 `0 `0 `0 `0 `0 `0 `0 `0 H*; %IENDB`formiko-1.3.0/icons/48x48/0000755000175000017500000000000013227326227015265 5ustar mcbigmcbig00000000000000formiko-1.3.0/icons/48x48/formiko.png0000644000175000017500000000444613160721436017446 0ustar mcbigmcbig00000000000000PNG  IHDR00WsBIT|dIDAThXml[W~W츮W7[<US*4*BZV15(T2ۘi &@. 4E[uI_׹~͜_]!>}<>X*Vg BB<hud2ظRD(zgtVDEh!p8477CEQ^QUgPhru/ p=I `͚5eIcPU5(`>JKtvvEqd2u|*, Z[[t:+B l6q`0z7^ʯ`HLVFq(χfX1˗/czz~O{{D8>WoiiyW5&IF&hDKK 8l2h .`ff!8q뮻 1== YX,BdYcؑǏ4SM$鉉˦{zҥFx<x<saڵ| `nnx;%IRy t8ESp˟n˖-MӁzL,׋FvlbSSg69EQBQ\.b9L&?g4 _ xJ1ͅ1$IpYb1X,h4rDSSSkD"aL&n\nYXX@X<{ƫy"ȗl̪p\HӰX,hkkZST x`pQJ1;; ۝hhhhzg˞WgϞy&iZR& B4΢X,rAe˗/y45}|DpQ;|͗@OOOf;d$IX,Y|d29 !MDy6 p\ppp8P(UU]'fv#b~~7ov@nnooo뼶\\?I&tꫯt? [n$W,ReG+r,;O* ~_{@XJgΜyGuj& d2U"Bn02.$I$q5P(,]M}4@UUd25 ,Kܽ{N\Γ翦(1h4|:;cBEQ{<s P_fD"S4eYvj63::Z} 822Z",ː$)f֯_L̀R_s!NUU2Ƃt,OեiUQveT*J{홊z_2@)`8Zl׮] Ya4G=c5Cų/=(fggL&|H$BxᇟھlUUa0⃃*)UUa2_1eZO(/bddΝC.C.&IP__߿q Z>V=}t+ U}Ax嗿ZKNEQbBᤦiO ekci]'N8ԍ qA0֎NޛMX R%t]g \S:ou-=c6[xܷĀBz{{i-YBt\nn%o ,..4-+>>`7~p% ycoouU>-83IENDB`formiko-1.3.0/icons/24x24/0000755000175000017500000000000013227326227015251 5ustar mcbigmcbig00000000000000formiko-1.3.0/icons/24x24/formiko.png0000644000175000017500000000175713160721436017434 0ustar mcbigmcbig00000000000000PNG  IHDRw=sBIT|dIDATHR]H#W=33ILKm5!j(Z$}hԾ>R肰 -Eq X[t]%̺d4q2K$FKmB={/?mpx<"cϋz_ K+ zz t]?eˑ[  ;L&7779J)<ŐP)x<>tD(Z@Nmۊ\yhiiurž#Xpttayo  vvv~udlχ*TVVɭV+^((rGU'gޠ%I( g'rU[[[ݥL&ktK$J,\c "BeWTTRt:555Yn7GJR`&6f[K3 MBHnw`QIIP[[:p8( _d2BE.|>oȲ\a&y>rVMU|!]{p*@ӴiwKtz4͛KKKp#MӞ.//7t8%I)H$C08}(D"7riکP0M0J^!}]@ 0Lzjj^Qo뺞_YY|hh(|>(Wu]( l6 պi_X,{B]TU]=Hx^)^Ӵ(򝝝 4 \[Ӵkfyy SQq<1?7t:߳X,]TIPUm]cgff~}]o=gIENDB`formiko-1.3.0/icons/32x32/0000755000175000017500000000000013227326227015247 5ustar mcbigmcbig00000000000000formiko-1.3.0/icons/32x32/formiko.png0000644000175000017500000000262613160721436017426 0ustar mcbigmcbig00000000000000PNG  IHDR szzsBIT|dMIDATXVkhW~wksm^HkK΂C\nӱTHݼ`*AyactgȜZ1F)ER;JӖ4i&i/w91+1MU0|yy OA?pRꠔV8 o{zzcQJ)`٠:,cG=z4zB Vk׮(Ⱥ󹹹~j мB?jJ), A(9 `0XC)u|߿Ujx<F !I\.L&2An߾X,4|bʆ);/MLL^s6mt=ЀfX,qLOOCU:YI<Ӓ$e9Եk׌^֙3g5U2::L&e l0+++u)#rdYޜd)V cl,ˇm۶m(u]W'Lmmmɓ'w>'R»v:K$c$zMc@Kn'􉅅>O!$a .3Rat]g @j N?(b$I_ l-xXhv,nX-;@yVGY5ݻ_o^SDՋIENDB`formiko-1.3.0/icons/svg2png.py0000644000175000017500000000061013160721436016420 0ustar mcbigmcbig00000000000000# -*- coding: utf-8 -*- from gi.repository.GdkPixbuf import Pixbuf from os import makedirs def main(): for size in (16, 22, 24, 32, 48, 64, 128, 256, 512): icon = Pixbuf.new_from_file_at_scale("formiko.svg", size, size, True) makedirs("%dx%d" % (size, size)) icon.savev("%dx%d/formiko.png" % (size, size), "png", [], []) if __name__ == "__main__": main() formiko-1.3.0/icons/formiko.svg0000644000175000017500000007476113160721436016671 0ustar mcbigmcbig00000000000000 image/svg+xml formiko-1.3.0/setup.py0000644000175000017500000000436113227323532015065 0ustar mcbigmcbig00000000000000#!/usr/bin/env python from setuptools import setup from io import open from formiko import __version__, __url__, __comment__ def doc(): with open("README.rst", "r", encoding="utf-8") as readme: return readme.read().strip() def icons_data(): path = 'share/icons/hicolor' icons = [("%s/scalable/apps" % path, ["icons/formiko.svg"])] for size in (16, 22, 24, 32, 48, 64, 128, 256, 512): icons.append(("%s/%dx%d/apps" % (path, size, size), ["icons/%dx%d/formiko.png" % (size, size)])) return icons setup( name="formiko", version=__version__, description=__comment__, author="Ondrej Tuma", author_email="mcbig@zeropage.cz", url=__url__, packages=['formiko'], data_files=[('share/doc/formiko', ['README.rst', 'COPYING', 'ChangeLog', 'AUTHORS']), ("share/applications", ["formiko.desktop", "formiko-vim.desktop"]), ('share/formiko/icons', ['icons/formiko.svg'])] + icons_data(), keywords=["doc", "html", "rst", "docutils", "md", "markdown", "editor"], license="BSD", long_description=doc(), classifiers=[ "Development Status :: 5 - Production/Stable", "Environment :: X11 Applications :: GTK", "Intended Audience :: Developers", "Intended Audience :: End Users/Desktop", "License :: OSI Approved :: BSD License", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Topic :: Documentation", "Topic :: Software Development :: Documentation", "Topic :: Text Editors :: Documentation", "Topic :: Text Editors :: Text Processing", "Topic :: Text Processing", "Topic :: Text Processing :: Markup", "Topic :: Text Processing :: Markup :: HTML", "Topic :: Utilities"], requires=['docutils (>= 0.12)', 'python_gi', 'webkit2', 'gtksourceview'], install_requires=['docutils >= 0.12'], entry_points={ 'gui_scripts': [ 'formiko = formiko.main:main', 'formiko-vim = formiko.main:main_vim' ] } )