formiko-1.4.3/0000755000175000017500000000000013573627650013367 5ustar mcbigmcbig00000000000000formiko-1.4.3/AUTHORS0000644000175000017500000000015413464027004014421 0ustar mcbigmcbig00000000000000Code ---- Ondřej Tůma Graphics -------- Petr Šimčík formiko-1.4.3/COPYING0000644000175000017500000000276013464027004014411 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.4.3/ChangeLog0000644000175000017500000000542213573531414015134 0ustar mcbigmcbig00000000000000Changelog ========= Version 1.4.3 * cz.zeropage.Formiko application ID fix Version 1.4.2 * save_as window fix * AppStream metadata Version 1.4.0 * man pages * words and characters count live info * Recommonmark (MarkDown) support with colored code * change preview - set paned position fix * GTK Warning fix (debian bug #933622) * SearchBar and searching text in editor * searching text in preview * creating backup file before write changes to new one * refresh preview action / button * opening links in system from preview, known file types open in new formiko window * preview auto scroll * fixing positing in preview only mode * preview/editor/both with shortcuts and store in cache * edited file change time check Version 1.3.0 * spell check support * display white chars switch * highlighting current line switch * printing document output * shortcuts dialog Version 1.2.1 * 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 * 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 * 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 * Formiko Vim entry point Version 1.0.1 * 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 * 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.4.3/MANIFEST.in0000644000175000017500000000035213464027004015107 0ustar mcbigmcbig00000000000000include *.py include MANIFEST.in include README.rst include COPYING include AUTHORS include ChangeLog include formiko.desktop include formiko-vim.desktop include formiko.rst include formiko-vim.rst graft icons global-exclude *~ *.swp formiko-1.4.3/PKG-INFO0000644000175000017500000001220613573627650014465 0ustar mcbigmcbig00000000000000Metadata-Version: 1.1 Name: formiko Version: 1.4.3 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: 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 with auto scroll * periodic save file * json and html preview * spell check * linked file opening 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 * docutils - reStrucured support recommended: ~~~~~~~~~~~~ * recommonmark - for Common Mark support (MarkDown) * Pygments - syntax color in html output code blocks optionally: ~~~~~~~~~~~ **Python**: * docutils-tinyhtmlwriter * 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.4.3/README.rst0000644000175000017500000000614413573626254015062 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 with auto scroll * periodic save file * json and html preview * spell check * linked file opening 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 * docutils - reStrucured support recommended: ~~~~~~~~~~~~ * recommonmark - for Common Mark support (MarkDown) * Pygments - syntax color in html output code blocks optionally: ~~~~~~~~~~~ **Python**: * docutils-tinyhtmlwriter * 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.4.3/formiko/0000755000175000017500000000000013573627650015035 5ustar mcbigmcbig00000000000000formiko-1.4.3/formiko/__init__.py0000644000175000017500000000040413573531352017135 0ustar mcbigmcbig00000000000000# -*- coding: utf-8 -*- __version__ = "1.4.3" __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.4.3/formiko/__main__.py0000644000175000017500000000005413464027004017110 0ustar mcbigmcbig00000000000000from formiko.main import main exit(main()) formiko-1.4.3/formiko/application.py0000644000175000017500000001260413573531273017710 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"]) self.set_accels_for_action("win.find-in-document", ["f"]) self.set_accels_for_action("win.find-next-match", ["g"]) self.set_accels_for_action("win.find-previous-match", ["g"]) self.set_accels_for_action("win.refresh-preview", ["r"]) formiko-1.4.3/formiko/dialogs.py0000644000175000017500000001120013561227673017021 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 FileNotFoundDialog(Gtk.MessageDialog): def __init__(self, parent, filename): super(FileNotFoundDialog, self).__init__( parent, Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.ERROR, Gtk.ButtonsType.CANCEL, "File `%s` not found" % filename) class FileChangedDialog(Gtk.MessageDialog): def __init__(self, parent, file_name): super(FileChangedDialog, self).__init__( parent, Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.INFO, Gtk.ButtonsType.YES_NO, "File `%s` was changed.\n" "Do you want to load from storage?" % file_name) 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.4.3/formiko/editor_actions.py0000644000175000017500000001152613464027004020404 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.4.3/formiko/icons.py0000644000175000017500000000321413464027004016504 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.4.3/formiko/main.py0000644000175000017500000000111213573531307016317 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) 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.4.3/formiko/menu.py0000644000175000017500000000163713556637325016363 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("Keyboard 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.4.3/formiko/preferences.py0000644000175000017500000001353313560471354017707 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) self.auto_scroll_btn = Gtk.CheckButton( label='Auto scroll', action_name="win.auto-scroll-toggle", action_target=Variant('b', True)) self.auto_scroll_btn.set_active(user_preferences.auto_scroll) vbox.pack_start(self.auto_scroll_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.4.3/formiko/renderer.py0000644000175000017500000003513213560313270017203 0ustar mcbigmcbig00000000000000# -*- coding: utf-8 -*- from gi import require_version from os.path import abspath, dirname, splitext, exists require_version('WebKit2', '4.0') # noqa from gi.repository.WebKit2 import WebView, PrintOperation, FindOptions from gi.repository.GLib import idle_add, Bytes, get_home_dir, \ log_default_handler, LogLevelFlags, MAXUINT, Error from gi.repository.Gtk import Overlay, Label, Align, main_iteration, \ show_uri_on_window 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 from formiko.dialogs import FileNotFoundDialog from formiko.sourceview import LANGS try: from docutils_tinyhtml import Writer as TinyWriter except ImportError: TinyWriter = None try: from docutils_html5 import Writer as Html5Writer except ImportError: Html5Writer = None try: from recommonmark.parser import CommonMarkParser from recommonmark.transform import AutoStructify, DummyStateMachine class StringStructify(AutoStructify): """Support AutoStructify for publish_string function.""" def apply(self): """Apply the transformation by configuration.""" file_name = self.document.settings.file_name self.url_resolver = self.config['url_resolver'] assert callable(self.url_resolver) self.state_machine = DummyStateMachine() self.current_level = 0 self.file_dir = abspath(dirname(file_name)) self.root_dir = self.file_dir self.traverse(self.document) class ExtendCommonMarkParser(CommonMarkParser): """CommonMarkParser with working AutoStructify.""" settings_spec = RstParser.settings_spec def get_transforms(self): return CommonMarkParser.get_transforms(self) + [StringStructify] except ImportError: ExtendCommonMarkParser = 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 class Env(object): """Empty class for env overriding.""" srcdir = '' PARSERS = { 'rst': { 'key': 'rst', 'title': 'Docutils reStructuredText parser', 'class': RstParser, 'url': 'http://docutils.sourceforge.net'}, 'md': { 'key': 'md', 'title': 'Common Mark parser', 'class': ExtendCommonMarkParser, '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'}, '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

""" NOT_IMPLEMENTED_ERROR = """

Library Error

Sorry about that. This seems to be not supported functionality in dependent library Reader or Writer

%s
""" EXCEPTION_ERROR = """

Exception Error!

%s
""" SCROLL = """ """ JS_SCROLL = """ window.scrollTo( 0, (document.documentElement.scrollHeight-window.innerHeight)*%f); """ JS_POSITION = """ window.scrollY/(document.documentElement.scrollHeight-window.innerHeight) """ MARKUP = """ %s """ class Renderer(Overlay): def __init__(self, win, parser='rst', writer='html4', style=''): super(Renderer, self).__init__() self.webview = WebView() self.webview.connect("mouse-target-changed", self.on_mouse) self.webview.connect("context-menu", self.on_context_menu) self.webview.connect("button-release-event", self.on_button_release) self.add(self.webview) controller = self.webview.get_find_controller() self.search_done = None controller.connect("found-text", self.on_found_text) controller.connect("failed-to-find-text", self.on_faild_to_find_text) self.label = Label() self.label.set_halign(Align.START) self.label.set_valign(Align.END) self.add_overlay(self.label) self.link_uri = None self.context_button = 3 # will be rewrite by real value self.set_writer(writer) self.set_parser(parser) self.style = style self.tab_width = 8 self.__win = win @property def position(self): self.__position = -1 self.webview.run_javascript( JS_POSITION, None, self.on_position_callback, None) while self.__position < 0: # this call at this place do problem, when Gdk.threads_init main_iteration() return self.__position def on_position_callback(self, webview, result, data): try: js_res = webview.run_javascript_finish(result) self.__position = js_res.get_js_value().to_double() except Error: self.__position = 0 def on_mouse(self, webview, hit_test_result, modifiers): self.link_uri = None if hit_test_result.context_is_link(): self.link_uri = hit_test_result.get_link_uri() text = "link: %s" % self.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.replace("&", "&")) self.label.show() def on_context_menu(self, webview, menu, event, hit_test_result): self.context_button = event.button.button return True # disable context menu for now def on_button_release(self, webview, event): """Catch release-button only when try to follow link. Context menu is catch by webview before this callback. """ if event.button == self.context_button: return True if self.link_uri: if self.link_uri.startswith("file://"): # try to open source self.find_and_opendocument(self.link_uri[7:]) else: show_uri_on_window(None, self.link_uri, 0) return True def find_and_opendocument(self, file_path): ext = splitext(file_path)[1] if not ext: for EXT in LANGS.keys(): tmp = file_path + EXT if exists(tmp): file_path = tmp ext = EXT break if ext in LANGS: self.__win.open_document(file_path) elif exists(file_path): show_uri_on_window(None, "file://"+file_path, 0) else: dialog = FileNotFoundDialog(self.__win, file_path) dialog.run() dialog.destroy() 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, 'file_name': self.file_name } 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 NotImplementedError: exc_str = format_exc() return False, NOT_IMPLEMENTED_ERROR % exc_str, '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: if self.pos > 1: # vim a, b = len(self.src[:self.pos]), len(self.src[self.pos:]) position = (float(a)/(a+b)) if a or b else 0 else: position = self.pos 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) def do_next_match(self, text): controller = self.webview.get_find_controller() if controller.get_search_text() != text: self.search_done = None controller.search(text, FindOptions.WRAP_AROUND, MAXUINT) while self.search_done is None: main_iteration() elif self.search_done: controller.search_next() return self.search_done def do_previous_match(self, text): controller = self.webview.get_find_controller() if controller.get_search_text() != text: self.search_done = None controller.search( text, FindOptions.WRAP_AROUND | FindOptions.BACKWARDS, MAXUINT) while self.search_done is None: main_iteration() elif self.search_done: controller.search_previous() return self.search_done def stop_search(self): controller = self.webview.get_find_controller() controller.search_finish() def on_found_text(self, controller, count): self.search_done = True def on_faild_to_find_text(self, controller): self.search_done = False def scroll_to_position(self, position): if position is not None: self.pos = position if self.pos > 1: # vim a, b = len(self.src[:self.pos]), len(self.src[self.pos:]) position = (float(a)/(a+b)) if a or b else 0 else: position = self.pos self.webview.run_javascript(JS_SCROLL % position, None, None, None) formiko-1.4.3/formiko/shortcuts.py0000644000175000017500000001012013556643451017435 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 FindGroup(ShortcutsGroup): def __init__(self): super(FindGroup, self).__init__(title="Find") self.add(ShortcutsShortcut( accelerator="f", title="Find in Document / Find another match in same way")) self.add(ShortcutsShortcut( accelerator="g", title="Find next match")) self.add(ShortcutsShortcut( accelerator="g", title="Find previous match")) 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 PreviewGroup(ShortcutsGroup): def __init__(self): super(PreviewGroup, self).__init__(title="Preview") self.add(ShortcutsShortcut( accelerator="r", title="Refresh preview")) self.add(ShortcutsShortcut( accelerator="e", title="Show editor only")) self.add(ShortcutsShortcut( accelerator="p", title="Show preview only")) self.add(ShortcutsShortcut( accelerator="b", title="Show both")) 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, max_height=12) sec.add(GeneralGroup(editor_type)) sec.add(PreviewGroup()) sec.add(FindGroup()) if editor_type == "source": sec.add(SourceGroup()) elif editor_type == "vim": sec.add(VimGroup()) self.add(sec) formiko-1.4.3/formiko/sourceview.py0000644000175000017500000002654213560562250017601 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, SearchContext, SearchSettings from gi.repository.GLib import get_home_dir, timeout_add_seconds, Variant, \ idle_add, timeout_add from gi.repository.GtkSpell import Checker from gi.repository import GObject from gi.repository import Gtk from os import rename, stat, fstat from os.path import splitext, basename, isfile, exists from io import open from traceback import format_exc from sys import version_info, stderr from formiko.dialogs import FileSaveDialog, TraceBackDialog, FileChangedDialog 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 __last_ctime = 0 __skip_period = 0 __gsignals__ = { 'file-type': (GObject.SIGNAL_RUN_FIRST, None, (str,)), 'scroll-changed': (GObject.SIGNAL_RUN_LAST, None, (float,)) } action_name = GObject.property(type=str) action_target = GObject.property(type=GObject.TYPE_VARIANT) def __init__(self, win, 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) # TODO: will work when FileSaver and FileLoader will be used # self.text_buffer.set_implicit_trailing_newline(False) self.source_view = View.new_with_buffer(self.text_buffer) adj = self.get_vadjustment() adj.connect("value-changed", self.on_scroll_changed) self.spellchecker = Checker() self.spellchecker.connect("language-changed", self.on_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) self.search_settings = SearchSettings(wrap_around=True) self.search_context = SearchContext.new( self.text_buffer, self.search_settings) self.search_iter = None self.__win = win timeout_add(200, self.check_in_thread) @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): adj = self.source_view.get_vadjustment() hight = self.get_allocated_height() value = adj.get_value() if value: return adj.get_value()/(adj.get_upper()-hight) return 0 @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 on_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 on_scroll_changed(self, *params): self.emit("scroll-changed", self.position) 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.on_language_changed(self.spellchecker, self.spellchecker.get_language()) self.spellchecker.attach(self.source_view) else: self.spellchecker.detach() self.on_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 check_in_thread(self): """This function is called from GLib.timeout_add""" if not self.__file_name: return try: last_ctime = stat(self.__file_name).st_ctime if last_ctime > self.__last_ctime: self.__skip_period = True dialog = FileChangedDialog(self.__win, self.__file_name) if dialog.run() == Gtk.ResponseType.YES: cursor = self.text_buffer.get_insert() offset = self.text_buffer.get_iter_at_mark( cursor).get_offset() self.read_from_file(self.__file_name, offset) dialog.destroy() self.__skip_period = False except OSError: pass # file switching when modify by another software timeout_add(200, self.check_in_thread) def period_save_thread(self): if self.period_save: if self.__file_name and self.is_modified \ and not self.__skip_period: idle_add(self.save_to_file) timeout_add_seconds(self.period_save, self.period_save_thread) def read_from_file(self, file_name, offset=0): 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.__last_ctime = fstat(src.fileno()).st_ctime self.text_buffer.set_modified(False) if offset > -1: cursor = self.text_buffer.get_iter_at_offset(offset) self.text_buffer.place_cursor(cursor) idle_add(self.scroll_to_cursor, cursor) def scroll_to_cursor(self, cursor): self.source_view.scroll_to_iter(cursor, 0, 1, 1, 1) def save_to_file(self): try: if exists(self.__file_name): rename(self.__file_name, "%s~" % self.__file_name) 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.__last_ctime = fstat(src.fileno()).st_ctime self.text_buffer.set_modified(False) except Exception: error = format_exc() if self.__win: md = TraceBackDialog(self.__win, error) md.run() md.destroy() stderr.write(error) stderr.flush() def save(self): if not self.__file_name: self.__file_name = self.get_new_file_name() self.emit("file_type", self.file_ext) if self.__file_name: self.save_to_file() def save_as(self): new_file_name = self.get_new_file_name() if new_file_name: self.__file_name = new_file_name self.emit("file_type", self.file_ext) self.save_to_file() def get_new_file_name(self): ret_val = '' dialog = FileSaveDialog(self.__win) # 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) def do_next_match(self, text): if self.search_settings.get_search_text() != text: self.search_settings.set_search_text(text) self.search_iter = self.text_buffer.get_iter_at_mark( self.text_buffer.get_insert()) elif self.search_iter: self.search_iter.forward_char() else: return False found, self.search_iter, end = self.search_context.forward( self.search_iter) if not found: self.search_iter = None return False self.source_view.scroll_to_iter(self.search_iter, 0, 1, 1, 1) self.text_buffer.place_cursor(self.search_iter) return True def do_previous_match(self, text): if self.search_settings.get_search_text() != text: self.search_settings.set_search_text(text) self.search_iter = self.text_buffer.get_iter_at_mark( self.text_buffer.get_insert()) elif not self.search_iter: return False found, start, self.search_iter = self.search_context.backward( self.search_iter) if not found: self.search_iter = None return False self.search_iter.backward_chars(len(text)) self.source_view.scroll_to_iter(self.search_iter, 0, 1, 1, 1) self.text_buffer.place_cursor(self.search_iter) return True def stop_search(self): self.search_settings.set_search_text(None) formiko-1.4.3/formiko/status_menu.py0000644000175000017500000001565513464027004017754 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.Box): css = Gtk.CssProvider() css.load_from_data(b"* {border-top: 1px solid #91918c; padding: 1px;}") def __init__(self, preferences): super(Statusbar, self).__init__(orientation=Gtk.Orientation.HORIZONTAL) ctx = self.get_style_context() ctx.add_provider(Statusbar.css, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) self.info_bar = self.create_info_bar() self.pack_start(self.info_bar, False, False, 10) btn = StatusMenuButton( "Line 1, Col 1", LineColPopover(preferences)) self.pack_end(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_end(self.width_btn, False, False, 1) self.editor_popover = self.create_editor_popover(preferences) self.editor_btn = StatusMenuButton( "Editor", self.editor_popover) self.pack_end(self.editor_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 def create_info_bar(self): bar = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) bar.words_count = Gtk.Label("0") bar.pack_start(bar.words_count, True, True, 0) bar.pack_start(Gtk.Label("words,"), True, True, 5) bar.chars_count = Gtk.Label("0") bar.pack_start(bar.chars_count, True, True, 0) bar.pack_start(Gtk.Label("characters"), True, True, 5) bar.show_all() return bar def set_words_count(self, count): self.info_bar.words_count.set_label(str(count)) def set_chars_count(self, count): self.info_bar.chars_count.set_label(str(count)) formiko-1.4.3/formiko/user.py0000644000175000017500000001223613556635047016371 0ustar mcbigmcbig00000000000000from os import makedirs from os.path import exists from traceback import print_exc from 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 ImportError: from ConfigParser import ConfigParser, NoSectionError, NoOptionError class View(): EDITOR = 1 PREVIEW = 2 BOTH = 3 def __new__(cls, value): value = int(value) assert 0 < value < 4 return value 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 auto_scroll = True 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, 'auto_scroll', smart_bool) 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, 'auto_scroll') 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 view = View.BOTH 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) cp.smart_get(self, 'view', View) 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)) cp.set('main', 'view', str(self.view)) 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.4.3/formiko/vim.py0000644000175000017500000000720013554376336016202 0ustar mcbigmcbig00000000000000# -*- coding: utf-8 -*- from gi.repository.Gtk import Socket from gi.repository.GObject import SIGNAL_RUN_FIRST, SIGNAL_RUN_LAST 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,)), 'scroll-changed': (SIGNAL_RUN_LAST, None, (float,)) # not implemented } 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.4.3/formiko/widgets.py0000644000175000017500000000123213547526725017055 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.4.3/formiko/window.py0000644000175000017500000006164013560361164016714 0ustar mcbigmcbig00000000000000# -*- coding: utf-8 -*- 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 re import compile as re_compile, U as re_U from enum import Enum from gi.repository import Gtk, GLib, Gio from formiko.vim import VimEditor from formiko.sourceview import SourceView, View as GtkSourceView from formiko.renderer import Renderer, EXTS, WebView as GtkWebView from formiko.dialogs import QuitDialogWithoutSave, FileOpenDialog, \ FileSaveDialog from formiko.preferences import Preferences from formiko.user import UserCache, UserPreferences, View 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 = 'Untitled Document' RE_WORD = re_compile(r'([\w]+)', re_U) RE_CHAR = re_compile(r'[\w \t\.,\?\(\)"\']', re_U) class SearchWay(Enum): NEXT = 0 PREVIOUS = 1 class AppWindow(Gtk.ApplicationWindow): def __init__(self, editor, file_name=''): assert editor in ('vim', 'source', None) self.runing = True self.editor_type = editor self.focused = None self.search_way = SearchWay.NEXT 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) action = Gio.SimpleAction.new("find-in-document", None) action.connect("activate", self.on_find_in_document) self.add_action(action) action = Gio.SimpleAction.new("find-next-match", None) action.connect("activate", self.on_find_next_match) self.add_action(action) action = Gio.SimpleAction.new("find-previous-match", None) action.connect("activate", self.on_find_previous_match) self.add_action(action) self.refresh_preview_action = Gio.SimpleAction.new( "refresh-preview", None) self.refresh_preview_action.connect( "activate", self.on_refresh_preview) self.add_action(self.refresh_preview_action) pref = self.preferences self.create_stateful_action( "switch-view-toggle", 'q', self.cache.view, self.on_switch_view_toggle) self.create_stateful_action( "change-preview", 'q', pref.preview, self.on_change_preview) self.create_stateful_action( "auto-scroll-toggle", 'b', pref.auto_scroll, self.on_auto_scroll_toggle) 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 open_document(self, file_path): if self.editor_type == 'source' and \ self.get_title() == NOT_SAVED_NAME: self.editor.read_from_file(file_path) else: for window in self.get_application().get_windows(): if file_path == window.file_path: window.present() return self.get_application().new_window(self.editor_type, file_path) 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: self.open_document(dialog.get_filename()) dialog.destroy() def on_save_document(self, action, *params): if self.editor_type == 'source': self.editor.save() def on_save_document_as(self, action, *params): if self.editor_type == 'source': self.editor.save_as() 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() self.cache.view = state if state == View.BOTH: self.editor.show() self.renderer.show() self.refresh_preview_action.set_enabled(True) self.both_toggle_btn.set_active(True) elif state == View.EDITOR: self.editor.show() self.renderer.hide() self.refresh_preview_action.set_enabled(False) self.editor_toggle_btn.set_active(True) else: self.editor.hide() self.renderer.show() self.refresh_preview_action.set_enabled(True) 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) self.preferences.preview = orientation self.preferences.save() GLib.idle_add( lambda: GLib.timeout_add(100, self.set_position) and False) def set_position(self): """Set position after change orientation. This must be do after some timeout. It is HARD FIX of gtk error https://gitlab.gnome.org/GNOME/gtk/issues/1959 """ if self.paned.get_orientation() == Gtk.Orientation.VERTICAL: self.paned.set_position(self.paned.get_allocated_height()/2) else: self.paned.set_position(self.paned.get_allocated_width()/2) def on_auto_scroll_toggle(self, action, param): auto_scroll = not self.preferences.auto_scroll self.preferences.auto_scroll = auto_scroll 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_scroll_changed(self, widget, position): if self.preferences.auto_scroll: self.renderer.scroll_to_position(position) 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 on_find_in_document(self, action, *param): if self.editor_type != 'source' and not self.renderer.props.visible: return # works only with source view or renderer if self.search.get_search_mode(): if self.search_way == SearchWay.NEXT: self.on_find_next_match(action, *param) else: self.on_find_previous_match(action, *param) else: self.search_way = SearchWay.NEXT self.focused = self.get_focus() self.search.set_search_mode(True) self.search_entry.grab_focus() def on_search_mode_changed(self, search_bar, param): if not self.search.get_search_mode(): if isinstance(self.focused, GtkSourceView): self.editor.stop_search() elif isinstance(self.focused, GtkWebView): self.renderer.stop_search() elif self.editor_type == "source" and self.editor.props.visible: self.editor.stop_search() elif self.renderer.props.visible: self.renderer.stop_search() self.focused.grab_focus() self.focused = None def on_search_mode_text(self, search_entry, param): if self.search_way == SearchWay.NEXT: res = self.on_find_next_match(None, param) else: res = self.on_find_previous_match(None, param) ctx = self.search_entry.get_style_context() if not res and self.search_entry.get_text(): Gtk.StyleContext.add_class(ctx, "error") else: Gtk.StyleContext.remove_class(ctx, "error") def on_find_next_match(self, action, *params): res = False if self.search.get_search_mode(): text = self.search_entry.get_text() if isinstance(self.focused, GtkSourceView): res = self.editor.do_next_match(text) elif isinstance(self.focused, GtkWebView): res = self.renderer.do_next_match(text) elif self.editor_type == "source" and self.editor.props.visible: res = self.editor.do_next_match(text) elif self.renderer.props.visible: res = self.renderer.do_next_match(text) return res def on_find_previous_match(self, action, *params): res = False if self.search.get_search_mode(): text = self.search_entry.get_text() if isinstance(self.focused, GtkSourceView): res = self.editor.do_previous_match(text) elif isinstance(self.focused, GtkWebView): res = self.renderer.do_previous_match(text) elif self.editor_type == "source" and self.editor.props.visible: res = self.editor.do_previous_match(text) elif self.renderer.props.visible: res = self.renderer.do_previous_match(text) return res def on_refresh_preview(self, action, *params): self.check_in_thread(True) 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) headerbar.pack_end(IconButton(symbol="view-refresh-symbolic", tooltip="Refresh preview", action_name="win.refresh-preview")) if self.editor_type: 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", use_underline=True, 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", use_underline=True, 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", use_underline=True, 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) else: self.editor = SourceView(self, self.preferences, "editor.spell-lang") self.insert_action_group("editor", EditorActionGroup(self.editor, self.renderer, self.preferences)) if file_name: self.editor.read_from_file(file_name) self.editor.connect("file-type", self.on_file_type) self.editor.connect("scroll-changed", self.on_scroll_changed) self.paned.pack1(self.editor, True, False) self.paned.pack2(self.renderer, True, False) if self.cache.view == View.EDITOR: self.renderer.show_all() self.renderer.hide() self.renderer.set_no_show_all(True) self.refresh_preview_action.set_enabled(False) elif self.cache.view == View.PREVIEW: self.editor.show_all() self.editor.hide() self.editor.set_no_show_all(True) 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) overlay = Gtk.Overlay() box.pack_start(overlay, True, True, 0) if self.editor_type: self.paned = Gtk.Paned(orientation=self.preferences.preview, position=self.cache.paned) overlay.add_overlay(self.paned) self.fill_panned(file_name) else: self.__file_name = file_name self.set_title(file_name) overlay.add_overlay(self.renderer) if self.cache.is_maximized: self.maximize() if self.editor_type == 'source': self.status_bar = Statusbar(self.preferences.editor) box.pack_end(self.status_bar, False, True, 0) self.search = Gtk.SearchBar() self.search.set_show_close_button(False) self.search.set_halign(Gtk.Align.CENTER) self.search.set_valign(Gtk.Align.START) self.search.connect("notify::search-mode-enabled", self.on_search_mode_changed) overlay.add_overlay(self.search) sbox = Gtk.Box(Gtk.Orientation.HORIZONTAL, 0) Gtk.StyleContext.add_class(sbox.get_style_context(), "linked") self.search.add(sbox) self.search_entry = Gtk.SearchEntry() self.search_entry.set_width_chars(30) sbox.pack_start(self.search_entry, True, True, 0) self.search.connect_entry(self.search_entry) self.search_entry.connect("notify::text", self.on_search_mode_text) prev_button = IconButton( symbol="go-previous-symbolic", tooltip="Previeous search", action_name="win.find-previous-match", focus_on_click=False) # set_focus_on_click(False) sbox.pack_start(prev_button, False, False, 0) next_button = IconButton( symbol="go-next-symbolic", tooltip="Next search", action_name="win.find-next-match", focus_on_click=False) sbox.pack_start(next_button, False, False, 0) def check_in_thread(self, force=False): if self.runing: if self.editor_type == 'vim': thread = Thread(target=self.refresh_from_vim, args=(force,)) thread.start() elif self.editor_type == 'source': GLib.idle_add(self.refresh_from_source, force) else: # self.editor = None GLib.idle_add(self.refresh_from_file, force) def not_running(self): if not self.runing: raise SystemExit(0) def refresh_from_vim(self, force): 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 force or 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, force): try: modified = self.editor.is_modified action = self.lookup_action("save-document") if action: # sometimes when closing window action is None action.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 force or last_changes > self.__last_changes: self.__last_changes = last_changes text = self.editor.text words_count = 0 for w in RE_WORD.finditer(text): words_count += 1 self.status_bar.set_words_count(words_count) chars_count = 0 for c in RE_CHAR.finditer(text): chars_count += 1 self.status_bar.set_chars_count(chars_count) self.renderer.render(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, force): try: last_changes = stat(self.__file_name).st_ctime if force or 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, self.renderer.position) except BaseException: print_exc() GLib.timeout_add(500, self.check_in_thread) @property def file_path(self): return self.editor.file_path if self.editor_type else self.__file_name formiko-1.4.3/formiko-vim.desktop0000644000175000017500000000054613464027004017210 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.4.3/formiko-vim.rst0000644000175000017500000000247313464027004016350 0ustar mcbigmcbig00000000000000formiko-vim =========== :manual_section: 1 :manual_group: General Commands Manual :date: 18 Jan 2018 :subtitle: reStructuredText and MarkDown editor and live previewer :author: Ondřej Tůma (mcbig@zeropage.cz) :version: {version} SYNOPSIS ~~~~~~~~ formiko-vim [options] [FILE] DESCRIPTION ~~~~~~~~~~~ Formiko is reStructuredText and MarkDown editor and live previewer. It is written in Python with Gtk3 and Webkit2. It use Docutils and recommonmark Common Mark parser. This version use Vim as editor. At this moment, **this works only on X11 graphics backend**, because GtkSockets not work on Wayland yet. OPTIONS ~~~~~~~ -h, --help Show help options :FILE: Specifies the file to open when formiko starts. If this is not specified, formiko will load a blank file with an "Untitled Document" label with ReStructuredText format. FILES ~~~~~ ~/.config/formiko.ini Your personal configuration file. Formiko store your options automatically when you make change. ~/.cache/formiko/window.ini Your personal cache file. Formiko store there values about window size and paned panels. ~/.cache/formiko/WebKitCache This is your WebKit preview cache. BUGS ~~~~ If you find a bug, please report it at the GitHub issues tracker. See https://github.com/ondratu/formiko/issues. SEE ALSO ~~~~~~~~ vim(1) formiko-1.4.3/formiko.desktop0000644000175000017500000000052313464027004016412 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.4.3/formiko.egg-info/0000755000175000017500000000000013573627650016527 5ustar mcbigmcbig00000000000000formiko-1.4.3/formiko.egg-info/PKG-INFO0000644000175000017500000001220613573627650017625 0ustar mcbigmcbig00000000000000Metadata-Version: 1.1 Name: formiko Version: 1.4.3 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: 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 with auto scroll * periodic save file * json and html preview * spell check * linked file opening 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 * docutils - reStrucured support recommended: ~~~~~~~~~~~~ * recommonmark - for Common Mark support (MarkDown) * Pygments - syntax color in html output code blocks optionally: ~~~~~~~~~~~ **Python**: * docutils-tinyhtmlwriter * 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.4.3/formiko.egg-info/SOURCES.txt0000644000175000017500000000163113573627650020414 0ustar mcbigmcbig00000000000000AUTHORS COPYING ChangeLog MANIFEST.in README.rst formiko-vim.desktop formiko-vim.rst formiko.desktop formiko.metainfo.xml formiko.rst 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.4.3/formiko.egg-info/dependency_links.txt0000644000175000017500000000000113573627650022575 0ustar mcbigmcbig00000000000000 formiko-1.4.3/formiko.egg-info/entry_points.txt0000644000175000017500000000011713573627650022024 0ustar mcbigmcbig00000000000000[gui_scripts] formiko = formiko.main:main formiko-vim = formiko.main:main_vim formiko-1.4.3/formiko.egg-info/requires.txt0000644000175000017500000000001713573627650021125 0ustar mcbigmcbig00000000000000docutils>=0.12 formiko-1.4.3/formiko.egg-info/top_level.txt0000644000175000017500000000001013573627650021250 0ustar mcbigmcbig00000000000000formiko formiko-1.4.3/formiko.metainfo.xml0000644000175000017500000000523113572261527017355 0ustar mcbigmcbig00000000000000 cz.zeropage.Formiko Formiko reStructuredText and MarkDown editor and live previewer Documentation Markup Editor Text MarkDown reStructuredText Office WordProcessor GNOME GTK text/plain text/html text/x-mardown text/x-rst application/json text/json https://github.com/ondratu/formiko/releases/tag/1.4.2 https://github.com/ondratu/formiko/releases/tag/1.4.0 https://github.com/ondratu/formiko https://github.com/ondratu/formiko/issues cz.zeropage.Formiko.desktop MIT BSD-3-Clause

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 with auto scroll
  • Periodic save file
  • Json and html preview
  • Spell check
  • Linked file opening
https://raw.githubusercontent.com/ondratu/formiko/master/screenshot_1024.png Ondřej Tůma mcbig@zeropage.cz
formiko-1.4.3/formiko.rst0000644000175000017500000000236213553507675015574 0ustar mcbigmcbig00000000000000formiko ======= :manual_section: 1 :manual_group: General Commands Manual :date: 18 Jan 2018 :subtitle: reStructuredText and MarkDown editor and live previewer :author: Ondřej Tůma (mcbig@zeropage.cz) :version: {version} SYNOPSIS ~~~~~~~~ formiko [options] [FILE] DESCRIPTION ~~~~~~~~~~~ Formiko is reStructuredText and MarkDown editor and live previewer. It is written in Python with Gtk3 and Webkit2. It use Docutils and recommonmark Common Mark parser. This version use SourceView as editor and GtkSpell3 as spell checker. OPTIONS ~~~~~~~ -h, --help Show help options -p, --preview Preview only :FILE: Specifies the file to open when formiko starts. If this is not specified, formiko will load a blank file with an "Untitled Document" label with ReStructuredText format. FILES ~~~~~ ~/.config/formiko.ini Your personal configuration file. Formiko store your options automatically when you make change. ~/.cache/formiko/window.ini Your personal cache file. Formiko store there values about window size and paned panels. ~/.cache/formiko/WebKitCache This is your WebKit preview cache. BUGS ~~~~ If you find a bug, please report it at the GitHub issues tracker. See https://github.com/ondratu/formiko/issues. formiko-1.4.3/icons/0000755000175000017500000000000013573627650014502 5ustar mcbigmcbig00000000000000formiko-1.4.3/icons/128x128/0000755000175000017500000000000013573627650015437 5ustar mcbigmcbig00000000000000formiko-1.4.3/icons/128x128/formiko.png0000644000175000017500000001603413464027004017601 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.4.3/icons/16x16/0000755000175000017500000000000013573627650015267 5ustar mcbigmcbig00000000000000formiko-1.4.3/icons/16x16/formiko.png0000644000175000017500000000115613464027004017430 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.4.3/icons/22x22/0000755000175000017500000000000013573627650015261 5ustar mcbigmcbig00000000000000formiko-1.4.3/icons/22x22/formiko.png0000644000175000017500000000155213464027004017422 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.4.3/icons/24x24/0000755000175000017500000000000013573627650015265 5ustar mcbigmcbig00000000000000formiko-1.4.3/icons/24x24/formiko.png0000644000175000017500000000175713464027004017435 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.4.3/icons/256x256/0000755000175000017500000000000013573627650015443 5ustar mcbigmcbig00000000000000formiko-1.4.3/icons/256x256/formiko.png0000644000175000017500000003623513464027004017612 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.4.3/icons/32x32/0000755000175000017500000000000013573627650015263 5ustar mcbigmcbig00000000000000formiko-1.4.3/icons/32x32/formiko.png0000644000175000017500000000262613464027004017427 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.4.3/icons/48x48/0000755000175000017500000000000013573627650015301 5ustar mcbigmcbig00000000000000formiko-1.4.3/icons/48x48/formiko.png0000644000175000017500000000444613464027004017447 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.4.3/icons/512x512/0000755000175000017500000000000013573627650015431 5ustar mcbigmcbig00000000000000formiko-1.4.3/icons/512x512/formiko.png0000644000175000017500000010031013464027004017562 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.4.3/icons/64x64/0000755000175000017500000000000013573627650015275 5ustar mcbigmcbig00000000000000formiko-1.4.3/icons/64x64/formiko.png0000644000175000017500000000627113464027004017441 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.4.3/icons/formiko.svg0000644000175000017500000007476113464027004016672 0ustar mcbigmcbig00000000000000 image/svg+xml formiko-1.4.3/icons/svg2png.py0000644000175000017500000000061013464027004016421 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.4.3/setup.cfg0000644000175000017500000000004613573627650015210 0ustar mcbigmcbig00000000000000[egg_info] tag_build = tag_date = 0 formiko-1.4.3/setup.py0000644000175000017500000001146213571274050015073 0ustar mcbigmcbig00000000000000#!/usr/bin/env python from setuptools import setup from docutils.core import publish_string from docutils.writers.manpage import Writer from io import open from gzip import open as zopen from distutils.command.build import build from distutils.command.clean import clean from distutils.command.install_data import install_data from distutils import log from os import path, makedirs, listdir from shutil import rmtree 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 def man_page(writer, src, dst): with open(src, encoding="utf-8") as source: rst = source.read().format(version=__version__) with zopen(dst, 'wb') as destination: destination.write(publish_string(source=rst, writer=writer)) class XBuild(build): def initialize_options(self): build.initialize_options(self) self.man_base = None def finalize_options(self): build.finalize_options(self) if self.man_base is None: self.man_base = path.join(self.build_base, 'man') def run(self): build.run(self) log.info("building man pages") if self.dry_run: return writer = Writer() if not path.exists(self.man_base): makedirs(self.man_base) for page in ('formiko', 'formiko-vim'): log.info('manpage %s.rst -> %s/%s.1.gz' % (page, self.man_base, page)) man_page(writer, page+'.rst', '%s/%s.1.gz' % (self.man_base, page)) class XClean(clean): def initialize_options(self): clean.initialize_options(self) self.man_base = None def finalize_options(self): clean.finalize_options(self) if self.man_base is None: self.man_base = path.join(self.build_base, 'man') def run(self): clean.run(self) log.info("clean man pages") if self.dry_run: return if path.exists(self.man_base): rmtree(self.man_base) class XInstallData(install_data): def initialize_options(self): install_data.initialize_options(self) self.man_base = None self.build_base = None def finalize_options(self): install_data.finalize_options(self) self.set_undefined_options('build', ('build_base', 'build_base')) if self.man_base is None: self.man_base = path.join(self.build_base, 'man') def run(self): self.data_files.append( ('share/man/man1', list("%s/%s" % (self.man_base, page) for page in listdir(self.man_base)))) install_data.run(self) 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/metainfo", ['formiko.metainfo.xml']), ('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' ] }, cmdclass={'build': XBuild, 'clean': XClean, 'install_data': XInstallData} )