2014-07-06 20:35:24 +00:00
|
|
|
|
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
|
2018-06-28 17:47:14 +00:00
|
|
|
|
# BEGIN LICENSE
|
2014-07-06 20:35:24 +00:00
|
|
|
|
# Copyright (C) 2012, Wolf Vollprecht <w.vollprecht@gmail.com>
|
|
|
|
|
# This program is free software: you can redistribute it and/or modify it
|
|
|
|
|
# under the terms of the GNU General Public License version 3, as published
|
|
|
|
|
# by the Free Software Foundation.
|
|
|
|
|
#
|
|
|
|
|
# This program is distributed in the hope that it will be useful, but
|
|
|
|
|
# WITHOUT ANY WARRANTY; without even the implied warranties of
|
|
|
|
|
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
|
|
|
|
|
# PURPOSE. See the GNU General Public License for more details.
|
|
|
|
|
#
|
|
|
|
|
# You should have received a copy of the GNU General Public License along
|
|
|
|
|
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
2018-06-28 17:47:14 +00:00
|
|
|
|
# END LICENSE
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
|
|
|
|
import codecs
|
2019-03-26 14:18:19 +00:00
|
|
|
|
import locale
|
2018-07-25 19:57:17 +00:00
|
|
|
|
import logging
|
2019-03-26 14:18:19 +00:00
|
|
|
|
import os
|
2018-06-28 17:38:43 +00:00
|
|
|
|
import re
|
2019-03-26 14:18:19 +00:00
|
|
|
|
import urllib
|
|
|
|
|
import webbrowser
|
2018-06-28 17:38:43 +00:00
|
|
|
|
from gettext import gettext as _
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2018-03-24 23:31:55 +00:00
|
|
|
|
import gi
|
2019-03-26 14:18:19 +00:00
|
|
|
|
|
2019-03-31 02:16:18 +00:00
|
|
|
|
from uberwriter.export_dialog import Export
|
|
|
|
|
from uberwriter.text_view import TextView
|
2019-03-26 14:18:19 +00:00
|
|
|
|
|
2019-01-27 15:42:36 +00:00
|
|
|
|
gi.require_version('Gtk', '3.0')
|
2019-01-27 19:20:56 +00:00
|
|
|
|
gi.require_version('WebKit2', '4.0') # pylint: disable=wrong-import-position
|
2019-03-23 17:05:02 +00:00
|
|
|
|
from gi.repository import Gtk, Gdk, GObject, GLib, Gio
|
2017-12-07 06:56:39 +00:00
|
|
|
|
from gi.repository import WebKit2 as WebKit
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
|
|
|
|
import cairo
|
|
|
|
|
|
2019-04-01 16:00:17 +00:00
|
|
|
|
from uberwriter import helpers
|
2019-04-01 20:40:59 +00:00
|
|
|
|
from uberwriter.theme import Theme
|
2019-04-01 16:00:17 +00:00
|
|
|
|
from uberwriter.helpers import get_builder
|
|
|
|
|
from uberwriter.gtkspellcheck import SpellChecker
|
2018-04-11 17:03:23 +00:00
|
|
|
|
|
2019-04-01 20:40:59 +00:00
|
|
|
|
from uberwriter.sidebar import Sidebar
|
|
|
|
|
from uberwriter.search_and_replace import SearchAndReplace
|
|
|
|
|
from uberwriter.settings import Settings
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2019-03-29 00:50:07 +00:00
|
|
|
|
from . import headerbars
|
|
|
|
|
|
2014-07-06 20:35:24 +00:00
|
|
|
|
# Some Globals
|
|
|
|
|
# TODO move them somewhere for better
|
|
|
|
|
# accesibility from other files
|
|
|
|
|
|
2018-07-25 19:57:17 +00:00
|
|
|
|
LOGGER = logging.getLogger('uberwriter')
|
|
|
|
|
|
2014-07-06 20:35:24 +00:00
|
|
|
|
CONFIG_PATH = os.path.expanduser("~/.config/uberwriter/")
|
|
|
|
|
|
2019-01-27 19:20:56 +00:00
|
|
|
|
|
2019-04-01 20:40:59 +00:00
|
|
|
|
class Window(Gtk.ApplicationWindow):
|
2019-03-29 01:30:43 +00:00
|
|
|
|
__gsignals__ = {
|
|
|
|
|
'save-file': (GObject.SIGNAL_ACTION, None, ()),
|
|
|
|
|
'open-file': (GObject.SIGNAL_ACTION, None, ()),
|
|
|
|
|
'save-file-as': (GObject.SIGNAL_ACTION, None, ()),
|
|
|
|
|
'new-file': (GObject.SIGNAL_ACTION, None, ()),
|
|
|
|
|
'toggle-bibtex': (GObject.SIGNAL_ACTION, None, ()),
|
|
|
|
|
'toggle-preview': (GObject.SIGNAL_ACTION, None, ()),
|
|
|
|
|
'close-window': (GObject.SIGNAL_ACTION, None, ())
|
|
|
|
|
}
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2018-10-14 15:46:30 +00:00
|
|
|
|
WORDCOUNT = re.compile(r"(?!\-\w)[\s#*\+\-]+", re.UNICODE)
|
2018-06-28 17:38:43 +00:00
|
|
|
|
|
2018-11-23 23:51:18 +00:00
|
|
|
|
def __init__(self, app):
|
2018-07-29 18:48:45 +00:00
|
|
|
|
"""Set up the main window"""
|
2018-10-14 15:46:30 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
Gtk.ApplicationWindow.__init__(self,
|
|
|
|
|
application=Gio.Application.get_default(),
|
|
|
|
|
title="Uberwriter")
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2019-03-29 01:30:43 +00:00
|
|
|
|
# Set UI
|
2019-04-15 02:01:59 +00:00
|
|
|
|
self.builder = get_builder('Window')
|
2019-03-31 02:16:18 +00:00
|
|
|
|
root = self.builder.get_object("FullscreenOverlay")
|
|
|
|
|
root.connect('style-updated', self.apply_current_theme)
|
|
|
|
|
self.add(root)
|
2018-10-14 15:46:30 +00:00
|
|
|
|
|
2019-03-31 02:16:18 +00:00
|
|
|
|
self.set_default_size(900, 500)
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2019-03-31 02:16:18 +00:00
|
|
|
|
# Preferences
|
2018-07-29 18:48:45 +00:00
|
|
|
|
self.settings = Settings.new()
|
2018-07-25 19:57:17 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
# Headerbars
|
2018-11-23 23:51:18 +00:00
|
|
|
|
self.headerbar = headerbars.MainHeaderbar(app)
|
2018-10-14 15:46:30 +00:00
|
|
|
|
self.set_titlebar(self.headerbar.hb_container)
|
2019-03-29 00:50:07 +00:00
|
|
|
|
self.fs_headerbar = headerbars.FullscreenHeaderbar(self.builder, app)
|
2018-07-25 19:57:17 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
self.title_end = " – UberWriter"
|
|
|
|
|
self.set_headerbar_title("New File" + self.title_end)
|
2018-07-25 19:57:17 +00:00
|
|
|
|
|
2018-10-14 15:46:30 +00:00
|
|
|
|
self.word_count = self.builder.get_object('word_count')
|
|
|
|
|
self.char_count = self.builder.get_object('char_count')
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
# Setup status bar hide after 3 seconds
|
2018-10-14 15:46:30 +00:00
|
|
|
|
self.status_bar = self.builder.get_object('status_bar_box')
|
|
|
|
|
self.statusbar_revealer = self.builder.get_object('status_bar_revealer')
|
2019-03-31 02:16:18 +00:00
|
|
|
|
self.status_bar.get_style_context().add_class('status-bar-box')
|
2018-07-29 18:48:45 +00:00
|
|
|
|
self.status_bar_visible = True
|
|
|
|
|
self.was_motion = True
|
|
|
|
|
self.buffer_modified_for_status_bar = False
|
2018-12-06 13:29:10 +00:00
|
|
|
|
|
2019-03-31 02:16:18 +00:00
|
|
|
|
self.timestamp_last_mouse_motion = 0
|
2018-12-06 13:29:10 +00:00
|
|
|
|
if self.settings.get_value("poll-motion"):
|
|
|
|
|
self.connect("motion-notify-event", self.on_motion_notify)
|
|
|
|
|
GObject.timeout_add(3000, self.poll_for_motion)
|
2018-07-25 19:57:17 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
self.accel_group = Gtk.AccelGroup()
|
|
|
|
|
self.add_accel_group(self.accel_group)
|
2018-07-25 19:57:17 +00:00
|
|
|
|
|
2019-03-24 05:25:51 +00:00
|
|
|
|
# Setup text editor
|
2019-03-31 02:16:18 +00:00
|
|
|
|
self.text_view = TextView()
|
|
|
|
|
self.text_view.props.halign = Gtk.Align.CENTER
|
|
|
|
|
self.text_view.connect('focus-out-event', self.focus_out)
|
|
|
|
|
self.text_view.show()
|
|
|
|
|
self.text_view.grab_focus()
|
|
|
|
|
|
|
|
|
|
self.text_view.get_buffer().connect('changed', self.on_text_changed)
|
2014-08-30 08:22:01 +00:00
|
|
|
|
|
2019-03-24 05:25:51 +00:00
|
|
|
|
# Setup preview webview
|
|
|
|
|
self.preview_webview = None
|
|
|
|
|
|
2018-10-14 15:46:30 +00:00
|
|
|
|
self.scrolled_window = self.builder.get_object('editor_scrolledwindow')
|
2019-03-31 02:16:18 +00:00
|
|
|
|
self.scrolled_window.get_style_context().add_class('uberwriter-scrolled-window')
|
|
|
|
|
self.scrolled_window.add(self.text_view)
|
2018-10-14 15:46:30 +00:00
|
|
|
|
self.editor_viewport = self.builder.get_object('editor_viewport')
|
2018-12-05 19:27:19 +00:00
|
|
|
|
|
2019-03-23 17:05:02 +00:00
|
|
|
|
# some people seems to have performance problems with the overlay.
|
2018-12-05 19:27:19 +00:00
|
|
|
|
# Let them disable it
|
2019-04-15 01:35:24 +00:00
|
|
|
|
self.overlay_id = None
|
|
|
|
|
self.toggle_gradient_overlay(self.settings.get_value("gradient-overlay"))
|
2014-10-02 17:02:59 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
# Init file name with None
|
|
|
|
|
self.set_filename()
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
# Setting up spellcheck
|
|
|
|
|
self.auto_correct = None
|
2019-01-27 15:42:36 +00:00
|
|
|
|
self.toggle_spellcheck(self.settings.get_value("spellcheck"))
|
2018-07-29 18:48:45 +00:00
|
|
|
|
self.did_change = False
|
2018-06-28 17:38:43 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
###
|
|
|
|
|
# Sidebar initialization test
|
|
|
|
|
###
|
2018-10-14 15:46:30 +00:00
|
|
|
|
self.paned_window = self.builder.get_object("main_pained")
|
|
|
|
|
self.sidebar_box = self.builder.get_object("sidebar_box")
|
2019-04-01 20:40:59 +00:00
|
|
|
|
self.sidebar = Sidebar(self)
|
2018-07-29 18:48:45 +00:00
|
|
|
|
self.sidebar_box.hide()
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
###
|
|
|
|
|
# Search and replace initialization
|
|
|
|
|
# Same interface as Sidebar ;)
|
|
|
|
|
###
|
2019-03-31 02:16:18 +00:00
|
|
|
|
self.searchreplace = SearchAndReplace(self, self.text_view)
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
# Window resize
|
|
|
|
|
self.window_resize(self)
|
|
|
|
|
self.connect("configure-event", self.window_resize)
|
|
|
|
|
self.connect("delete-event", self.on_delete_called)
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2019-03-29 01:30:43 +00:00
|
|
|
|
# Set current theme
|
|
|
|
|
self.apply_current_theme()
|
2019-03-31 02:16:18 +00:00
|
|
|
|
self.get_style_context().add_class('uberwriter-window')
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2019-03-29 01:30:43 +00:00
|
|
|
|
def apply_current_theme(self, *_):
|
|
|
|
|
"""Adjusts the window, CSD and preview for the current theme.
|
2019-03-26 14:18:19 +00:00
|
|
|
|
"""
|
2019-03-29 01:30:43 +00:00
|
|
|
|
# Get current theme
|
2019-04-14 00:58:45 +00:00
|
|
|
|
theme, changed = Theme.get_current_changed()
|
2019-03-29 01:30:43 +00:00
|
|
|
|
if changed:
|
|
|
|
|
# Set theme variant (dark/light)
|
|
|
|
|
Gtk.Settings.get_default().set_property(
|
|
|
|
|
"gtk-application-prefer-dark-theme",
|
|
|
|
|
GLib.Variant("b", theme.is_dark))
|
|
|
|
|
|
|
|
|
|
# Set theme css
|
|
|
|
|
style_provider = Gtk.CssProvider()
|
|
|
|
|
style_provider.load_from_path(theme.gtk_css_path)
|
|
|
|
|
Gtk.StyleContext.add_provider_for_screen(
|
|
|
|
|
self.get_screen(), style_provider,
|
|
|
|
|
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
|
|
|
|
|
|
|
|
|
|
# Reload preview if it exists
|
2019-04-15 01:35:24 +00:00
|
|
|
|
self.reload_preview()
|
2019-03-29 01:30:43 +00:00
|
|
|
|
|
|
|
|
|
# Redraw contents of window
|
|
|
|
|
self.queue_draw()
|
2019-03-26 14:18:19 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
def update_line_and_char_count(self):
|
|
|
|
|
"""it... it updates line and characters count
|
2018-07-25 19:57:17 +00:00
|
|
|
|
"""
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
if self.status_bar_visible is False:
|
2014-07-06 20:35:24 +00:00
|
|
|
|
return
|
2019-03-31 02:16:18 +00:00
|
|
|
|
text = self.text_view.get_text()
|
|
|
|
|
self.char_count.set_text(str(len(text)))
|
2018-07-29 18:48:45 +00:00
|
|
|
|
words = re.split(self.WORDCOUNT, text)
|
|
|
|
|
length = len(words)
|
|
|
|
|
# Last word a "space"
|
|
|
|
|
if not words[-1]:
|
|
|
|
|
length = length - 1
|
|
|
|
|
# First word a "space" (happens in focus mode...)
|
|
|
|
|
if not words[0]:
|
|
|
|
|
length = length - 1
|
|
|
|
|
if length == -1:
|
|
|
|
|
length = 0
|
|
|
|
|
self.word_count.set_text(str(length))
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2019-03-31 02:16:18 +00:00
|
|
|
|
def on_text_changed(self, *_args):
|
2018-07-29 18:48:45 +00:00
|
|
|
|
"""called when the text changes, sets the self.did_change to true and
|
|
|
|
|
updates the title and the counters to reflect that
|
2018-07-25 19:57:17 +00:00
|
|
|
|
"""
|
2014-08-08 11:25:57 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
if self.did_change is False:
|
|
|
|
|
self.did_change = True
|
|
|
|
|
title = self.get_title()
|
|
|
|
|
self.set_headerbar_title("* " + title)
|
|
|
|
|
|
|
|
|
|
self.buffer_modified_for_status_bar = True
|
|
|
|
|
self.update_line_and_char_count()
|
|
|
|
|
|
2019-03-27 02:29:29 +00:00
|
|
|
|
def set_fullscreen(self, state):
|
2018-07-29 18:48:45 +00:00
|
|
|
|
"""Puts the application in fullscreen mode and show/hides
|
|
|
|
|
the poller for motion in the top border
|
2018-07-25 19:57:17 +00:00
|
|
|
|
|
|
|
|
|
Arguments:
|
2018-07-29 18:48:45 +00:00
|
|
|
|
state {almost bool} -- The desired fullscreen state of the window
|
2018-07-25 19:57:17 +00:00
|
|
|
|
"""
|
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
if state.get_boolean():
|
|
|
|
|
self.fullscreen()
|
2018-10-14 15:46:30 +00:00
|
|
|
|
self.fs_headerbar.events.show()
|
2014-10-02 17:02:59 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
else:
|
|
|
|
|
self.unfullscreen()
|
2018-10-14 15:46:30 +00:00
|
|
|
|
self.fs_headerbar.events.hide()
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2019-03-31 02:16:18 +00:00
|
|
|
|
self.text_view.grab_focus()
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2019-03-27 02:29:29 +00:00
|
|
|
|
def set_focus_mode(self, state):
|
2018-07-29 18:48:45 +00:00
|
|
|
|
"""toggle focusmode
|
|
|
|
|
"""
|
2018-06-28 17:47:14 +00:00
|
|
|
|
|
2019-03-31 02:16:18 +00:00
|
|
|
|
focus_mode = state.get_boolean()
|
|
|
|
|
self.text_view.set_focus_mode(focus_mode)
|
|
|
|
|
if self.spell_checker:
|
|
|
|
|
self.spell_checker._misspelled.set_property('underline', 0 if focus_mode else 4)
|
|
|
|
|
self.text_view.grab_focus()
|
2018-07-25 19:57:17 +00:00
|
|
|
|
|
2019-03-27 02:29:29 +00:00
|
|
|
|
def set_hemingway_mode(self, state):
|
|
|
|
|
"""toggle hemingwaymode
|
|
|
|
|
"""
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2019-03-31 02:16:18 +00:00
|
|
|
|
self.text_view.set_hemingway_mode(state.get_boolean())
|
|
|
|
|
self.text_view.grab_focus()
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2019-04-17 00:03:37 +00:00
|
|
|
|
def window_resize(self, window, event=None):
|
2018-07-29 18:48:45 +00:00
|
|
|
|
"""set paddings dependant of the window size
|
|
|
|
|
"""
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2019-03-31 02:16:18 +00:00
|
|
|
|
# Adjust text editor width depending on window width, so that:
|
|
|
|
|
# - The number of characters per line is adequate (http://webtypography.net/2.1.2)
|
|
|
|
|
# - The number of characters stays constant while resizing the window / font
|
|
|
|
|
# - There is enough text margin for MarkupBuffer to apply indents / negative margins
|
2019-04-17 00:03:37 +00:00
|
|
|
|
#
|
|
|
|
|
# TODO: Avoid hard-coding. Font size is clearer than unclear dimensions, but not ideal.
|
|
|
|
|
w_width = event.width if event else window.get_allocation().width
|
2018-07-29 18:48:45 +00:00
|
|
|
|
if w_width < 900:
|
2019-04-17 00:03:37 +00:00
|
|
|
|
font_size = 14
|
2018-07-29 18:48:45 +00:00
|
|
|
|
self.get_style_context().add_class("small")
|
2019-03-31 02:16:18 +00:00
|
|
|
|
self.get_style_context().remove_class("large")
|
2018-07-29 18:48:45 +00:00
|
|
|
|
|
2019-04-17 00:03:37 +00:00
|
|
|
|
elif w_width < 1280:
|
|
|
|
|
font_size = 16
|
2018-07-29 18:48:45 +00:00
|
|
|
|
self.get_style_context().remove_class("small")
|
|
|
|
|
self.get_style_context().remove_class("large")
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
|
|
|
|
else:
|
2019-04-17 00:03:37 +00:00
|
|
|
|
font_size = 18
|
2018-07-29 18:48:45 +00:00
|
|
|
|
self.get_style_context().remove_class("small")
|
|
|
|
|
self.get_style_context().add_class("large")
|
2014-08-30 08:22:01 +00:00
|
|
|
|
|
2019-04-17 00:03:37 +00:00
|
|
|
|
font_width = int(font_size * 1/1.6) # Ratio specific to Fira Mono
|
|
|
|
|
width = 67 * font_width - 1 # 66 characters
|
|
|
|
|
horizontal_margin = 8 * font_width # 8 characters
|
|
|
|
|
width_request = width + horizontal_margin * 2
|
|
|
|
|
|
2019-03-31 02:16:18 +00:00
|
|
|
|
if self.text_view.props.width_request != width_request:
|
|
|
|
|
self.text_view.props.width_request = width_request
|
2019-04-17 00:03:37 +00:00
|
|
|
|
self.text_view.set_left_margin(horizontal_margin)
|
|
|
|
|
self.text_view.set_right_margin(horizontal_margin)
|
2018-07-29 18:48:45 +00:00
|
|
|
|
self.scrolled_window.props.width_request = width_request
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
# TODO: refactorizable
|
|
|
|
|
def save_document(self, _widget=None, _data=None):
|
|
|
|
|
"""provide to the user a filechooser and save the document
|
|
|
|
|
where he wants. Call set_headbar_title after that
|
|
|
|
|
"""
|
2014-10-04 22:18:17 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
if self.filename:
|
|
|
|
|
LOGGER.info("saving")
|
|
|
|
|
filename = self.filename
|
|
|
|
|
file_to_save = codecs.open(filename, encoding="utf-8", mode='w')
|
2019-03-31 02:16:18 +00:00
|
|
|
|
file_to_save.write(self.text_view.get_text())
|
2018-07-29 18:48:45 +00:00
|
|
|
|
file_to_save.close()
|
|
|
|
|
if self.did_change:
|
|
|
|
|
self.did_change = False
|
|
|
|
|
title = self.get_title()
|
|
|
|
|
self.set_headerbar_title(title[2:])
|
|
|
|
|
return Gtk.ResponseType.OK
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
filefilter = Gtk.FileFilter.new()
|
|
|
|
|
filefilter.add_mime_type('text/x-markdown')
|
|
|
|
|
filefilter.add_mime_type('text/plain')
|
2019-03-28 17:04:01 +00:00
|
|
|
|
filefilter.set_name('Markdown (.md)')
|
2018-07-29 18:48:45 +00:00
|
|
|
|
filechooser = Gtk.FileChooserDialog(
|
|
|
|
|
_("Save your File"),
|
|
|
|
|
self,
|
|
|
|
|
Gtk.FileChooserAction.SAVE,
|
|
|
|
|
("_Cancel", Gtk.ResponseType.CANCEL,
|
|
|
|
|
"_Save", Gtk.ResponseType.OK)
|
|
|
|
|
)
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
filechooser.set_do_overwrite_confirmation(True)
|
|
|
|
|
filechooser.add_filter(filefilter)
|
|
|
|
|
response = filechooser.run()
|
|
|
|
|
if response == Gtk.ResponseType.OK:
|
|
|
|
|
filename = filechooser.get_filename()
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
if filename[-3:] != ".md":
|
|
|
|
|
filename = filename + ".md"
|
|
|
|
|
try:
|
|
|
|
|
self.recent_manager.add_item("file:/ " + filename)
|
|
|
|
|
except:
|
|
|
|
|
pass
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
file_to_save = codecs.open(filename, encoding="utf-8", mode='w')
|
2019-03-31 02:16:18 +00:00
|
|
|
|
file_to_save.write(self.text_view.get_text())
|
2018-07-29 18:48:45 +00:00
|
|
|
|
file_to_save.close()
|
2018-07-25 19:57:17 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
self.set_filename(filename)
|
|
|
|
|
self.set_headerbar_title(
|
|
|
|
|
os.path.basename(filename) + self.title_end)
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
self.did_change = False
|
|
|
|
|
filechooser.destroy()
|
|
|
|
|
return response
|
|
|
|
|
|
|
|
|
|
filechooser.destroy()
|
|
|
|
|
return Gtk.ResponseType.CANCEL
|
|
|
|
|
|
|
|
|
|
def save_document_as(self, _widget=None, _data=None):
|
|
|
|
|
"""provide to the user a filechooser and save the document
|
|
|
|
|
where he wants. Call set_headbar_title after that
|
2018-07-25 19:57:17 +00:00
|
|
|
|
"""
|
2018-07-29 18:48:45 +00:00
|
|
|
|
filechooser = Gtk.FileChooserDialog(
|
|
|
|
|
"Save your File",
|
|
|
|
|
self,
|
|
|
|
|
Gtk.FileChooserAction.SAVE,
|
|
|
|
|
("_Cancel", Gtk.ResponseType.CANCEL,
|
|
|
|
|
"_Save", Gtk.ResponseType.OK)
|
|
|
|
|
)
|
|
|
|
|
filechooser.set_do_overwrite_confirmation(True)
|
|
|
|
|
if self.filename:
|
|
|
|
|
filechooser.set_filename(self.filename)
|
|
|
|
|
response = filechooser.run()
|
|
|
|
|
if response == Gtk.ResponseType.OK:
|
2018-07-25 19:57:17 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
filename = filechooser.get_filename()
|
|
|
|
|
if filename[-3:] != ".md":
|
|
|
|
|
filename = filename + ".md"
|
|
|
|
|
try:
|
|
|
|
|
self.recent_manager.remove_item("file:/" + filename)
|
|
|
|
|
self.recent_manager.add_item("file:/ " + filename)
|
|
|
|
|
except:
|
|
|
|
|
pass
|
2018-07-17 10:47:47 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
file_to_save = codecs.open(filename, encoding="utf-8", mode='w')
|
2019-03-31 02:16:18 +00:00
|
|
|
|
file_to_save.write(self.text_view.get_text())
|
2018-07-29 18:48:45 +00:00
|
|
|
|
file_to_save.close()
|
2018-07-25 19:57:17 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
self.set_filename(filename)
|
|
|
|
|
self.set_headerbar_title(
|
|
|
|
|
os.path.basename(filename) + self.title_end)
|
2018-07-25 19:57:17 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
try:
|
|
|
|
|
self.recent_manager.add_item(filename)
|
|
|
|
|
except:
|
|
|
|
|
pass
|
2018-07-17 10:47:47 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
filechooser.destroy()
|
|
|
|
|
self.did_change = False
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
else:
|
|
|
|
|
filechooser.destroy()
|
|
|
|
|
return Gtk.ResponseType.CANCEL
|
2018-07-25 19:57:17 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
def copy_html_to_clipboard(self, _widget=None, _date=None):
|
|
|
|
|
"""Copies only html without headers etc. to Clipboard
|
2018-07-25 19:57:17 +00:00
|
|
|
|
"""
|
|
|
|
|
|
2019-04-14 00:58:45 +00:00
|
|
|
|
output = helpers.pandoc_convert(self.text_view.get_text())
|
2018-07-29 18:48:45 +00:00
|
|
|
|
clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
|
2019-04-14 00:58:45 +00:00
|
|
|
|
clipboard.set_text(output, -1)
|
2018-07-29 18:48:45 +00:00
|
|
|
|
clipboard.store()
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
def open_document(self, _widget=None):
|
|
|
|
|
"""open the desired file
|
|
|
|
|
"""
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
if self.check_change() == Gtk.ResponseType.CANCEL:
|
|
|
|
|
return
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2019-04-14 01:03:13 +00:00
|
|
|
|
markdown_filter = Gtk.FileFilter.new()
|
|
|
|
|
markdown_filter.add_mime_type('text/markdown')
|
|
|
|
|
markdown_filter.add_mime_type('text/x-markdown')
|
|
|
|
|
markdown_filter.set_name(_('Markdown Files'))
|
|
|
|
|
|
|
|
|
|
plaintext_filter = Gtk.FileFilter.new()
|
|
|
|
|
plaintext_filter.add_mime_type('text/plain')
|
|
|
|
|
plaintext_filter.set_name(_('Plain Text Files'))
|
2018-07-25 19:57:17 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
filechooser = Gtk.FileChooserDialog(
|
2019-04-14 00:58:45 +00:00
|
|
|
|
_("Open a .md file"),
|
2018-07-29 18:48:45 +00:00
|
|
|
|
self,
|
|
|
|
|
Gtk.FileChooserAction.OPEN,
|
|
|
|
|
("_Cancel", Gtk.ResponseType.CANCEL,
|
|
|
|
|
"_Open", Gtk.ResponseType.OK)
|
|
|
|
|
)
|
2019-04-14 01:03:13 +00:00
|
|
|
|
filechooser.add_filter(markdown_filter)
|
|
|
|
|
filechooser.add_filter(plaintext_filter)
|
2018-07-29 18:48:45 +00:00
|
|
|
|
response = filechooser.run()
|
|
|
|
|
if response == Gtk.ResponseType.OK:
|
|
|
|
|
filename = filechooser.get_filename()
|
|
|
|
|
self.load_file(filename)
|
|
|
|
|
filechooser.destroy()
|
|
|
|
|
|
|
|
|
|
elif response == Gtk.ResponseType.CANCEL:
|
|
|
|
|
filechooser.destroy()
|
|
|
|
|
|
|
|
|
|
def check_change(self):
|
|
|
|
|
"""Show dialog to prevent loss of unsaved changes
|
2018-07-25 19:57:17 +00:00
|
|
|
|
"""
|
|
|
|
|
|
2019-03-31 02:16:18 +00:00
|
|
|
|
if self.did_change and self.text_view.get_text():
|
2018-07-29 18:48:45 +00:00
|
|
|
|
dialog = Gtk.MessageDialog(self,
|
|
|
|
|
Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
|
|
|
|
|
Gtk.MessageType.WARNING,
|
|
|
|
|
Gtk.ButtonsType.NONE,
|
|
|
|
|
_("You have not saved your changes.")
|
|
|
|
|
)
|
2019-03-31 02:16:18 +00:00
|
|
|
|
dialog.add_button(_("Close without saving"), Gtk.ResponseType.NO)
|
2018-07-29 18:48:45 +00:00
|
|
|
|
dialog.add_button(_("Cancel"), Gtk.ResponseType.CANCEL)
|
|
|
|
|
dialog.add_button(_("Save now"), Gtk.ResponseType.YES)
|
2018-12-14 19:47:15 +00:00
|
|
|
|
# dialog.set_default_size(200, 60)
|
2018-07-29 18:48:45 +00:00
|
|
|
|
dialog.set_default_response(Gtk.ResponseType.YES)
|
|
|
|
|
response = dialog.run()
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
if response == Gtk.ResponseType.YES:
|
|
|
|
|
if self.save_document() == Gtk.ResponseType.CANCEL:
|
|
|
|
|
dialog.destroy()
|
|
|
|
|
return self.check_change()
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
dialog.destroy()
|
|
|
|
|
return response
|
|
|
|
|
if response == Gtk.ResponseType.NO:
|
|
|
|
|
dialog.destroy()
|
|
|
|
|
return response
|
2018-06-26 23:46:01 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
dialog.destroy()
|
|
|
|
|
return Gtk.ResponseType.CANCEL
|
2014-09-01 21:07:18 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
def new_document(self, _widget=None):
|
|
|
|
|
"""create new document
|
2018-07-25 19:57:17 +00:00
|
|
|
|
"""
|
2014-09-01 21:07:18 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
if self.check_change() == Gtk.ResponseType.CANCEL:
|
|
|
|
|
return
|
2019-03-31 02:16:18 +00:00
|
|
|
|
self.text_view.clear()
|
2014-09-01 21:07:18 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
self.did_change = False
|
|
|
|
|
self.set_filename()
|
|
|
|
|
self.set_headerbar_title(_("New File") + self.title_end)
|
2018-06-28 17:47:14 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
def menu_toggle_sidebar(self, _widget=None):
|
|
|
|
|
"""WIP
|
|
|
|
|
"""
|
|
|
|
|
self.sidebar.toggle_sidebar()
|
2014-08-30 08:22:01 +00:00
|
|
|
|
|
2019-04-15 01:35:24 +00:00
|
|
|
|
def toggle_spellcheck(self, state):
|
2018-07-29 18:48:45 +00:00
|
|
|
|
"""Enable/disable the autospellchecking
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
Arguments:
|
|
|
|
|
status {gtk bool} -- Desired status of the spellchecking
|
2018-07-25 19:57:17 +00:00
|
|
|
|
"""
|
2018-06-28 17:47:14 +00:00
|
|
|
|
|
2019-04-15 01:35:24 +00:00
|
|
|
|
if state.get_boolean():
|
2018-07-29 18:48:45 +00:00
|
|
|
|
try:
|
2019-01-27 15:42:36 +00:00
|
|
|
|
self.spell_checker.enable()
|
2018-07-29 18:48:45 +00:00
|
|
|
|
except:
|
2019-01-27 15:42:36 +00:00
|
|
|
|
try:
|
|
|
|
|
self.spell_checker = SpellChecker(
|
2019-03-31 02:16:18 +00:00
|
|
|
|
self.text_view, locale.getdefaultlocale()[0],
|
2019-01-27 19:20:56 +00:00
|
|
|
|
collapse=False)
|
2019-01-27 15:42:36 +00:00
|
|
|
|
if self.auto_correct:
|
|
|
|
|
self.auto_correct.set_language(self.spell_checker.language)
|
2019-01-27 19:20:56 +00:00
|
|
|
|
self.spell_checker.connect_language_change( # pylint: disable=no-member
|
2019-01-27 15:42:36 +00:00
|
|
|
|
self.auto_correct.set_language)
|
|
|
|
|
except:
|
|
|
|
|
self.spell_checker = None
|
|
|
|
|
dialog = Gtk.MessageDialog(self,
|
|
|
|
|
Gtk.DialogFlags.MODAL \
|
|
|
|
|
| Gtk.DialogFlags.DESTROY_WITH_PARENT,
|
|
|
|
|
Gtk.MessageType.INFO,
|
|
|
|
|
Gtk.ButtonsType.NONE,
|
|
|
|
|
_("You can not enable the Spell Checker.")
|
|
|
|
|
)
|
|
|
|
|
dialog.format_secondary_text(
|
2019-03-31 02:16:18 +00:00
|
|
|
|
_("Please install 'hunspell' or 'aspell' dictionaries"
|
2019-01-27 15:42:36 +00:00
|
|
|
|
+ " for your language from the software center."))
|
|
|
|
|
_response = dialog.run()
|
2018-07-29 18:48:45 +00:00
|
|
|
|
return
|
2019-01-27 15:42:36 +00:00
|
|
|
|
return
|
|
|
|
|
else:
|
|
|
|
|
try:
|
|
|
|
|
self.spell_checker.disable()
|
|
|
|
|
except:
|
|
|
|
|
pass
|
2018-07-29 18:48:45 +00:00
|
|
|
|
return
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2019-04-15 01:35:24 +00:00
|
|
|
|
def toggle_gradient_overlay(self, state):
|
|
|
|
|
"""Toggle the gradient overlay
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
state {gtk bool} -- Desired state of the gradient overlay (enabled/disabled)
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
if state.get_boolean():
|
|
|
|
|
self.overlay_id = self.scrolled_window.connect_after("draw", self.draw_gradient)
|
|
|
|
|
elif self.overlay_id:
|
|
|
|
|
self.scrolled_window.disconnect(self.overlay_id)
|
|
|
|
|
|
2019-03-24 04:57:06 +00:00
|
|
|
|
def toggle_preview(self, state):
|
2018-07-29 18:48:45 +00:00
|
|
|
|
"""Toggle the preview mode
|
2018-06-28 17:47:14 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
Arguments:
|
|
|
|
|
state {gtk bool} -- Desired state of the preview mode (enabled/disabled)
|
|
|
|
|
"""
|
2018-06-28 00:03:48 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
if state.get_boolean():
|
2019-03-24 05:25:51 +00:00
|
|
|
|
self.show_preview()
|
|
|
|
|
else:
|
|
|
|
|
self.show_text_editor()
|
|
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def show_text_editor(self):
|
|
|
|
|
self.scrolled_window.remove(self.scrolled_window.get_child())
|
2019-03-31 02:16:18 +00:00
|
|
|
|
self.scrolled_window.add(self.text_view)
|
|
|
|
|
self.text_view.show()
|
2019-03-24 05:25:51 +00:00
|
|
|
|
self.preview_webview.destroy()
|
|
|
|
|
self.preview_webview = None
|
|
|
|
|
self.queue_draw()
|
2018-06-30 00:11:47 +00:00
|
|
|
|
|
2019-03-24 05:25:51 +00:00
|
|
|
|
def show_preview(self, loaded=False):
|
|
|
|
|
if loaded:
|
|
|
|
|
self.scrolled_window.remove(self.scrolled_window.get_child())
|
|
|
|
|
self.scrolled_window.add(self.preview_webview)
|
|
|
|
|
self.preview_webview.show()
|
|
|
|
|
self.queue_draw()
|
|
|
|
|
else:
|
2019-04-14 00:58:45 +00:00
|
|
|
|
args = ['--standalone',
|
2018-07-29 18:48:45 +00:00
|
|
|
|
'--mathjax',
|
2019-04-14 00:58:45 +00:00
|
|
|
|
'--css=' + Theme.get_current().web_css_path,
|
2019-03-26 14:18:19 +00:00
|
|
|
|
'--lua-filter=' + helpers.get_script_path('relative_to_absolute.lua'),
|
2018-07-29 18:48:45 +00:00
|
|
|
|
'--lua-filter=' + helpers.get_script_path('task-list.lua')]
|
2019-04-14 00:58:45 +00:00
|
|
|
|
output = helpers.pandoc_convert(self.text_view.get_text(), to="html5", args=args)
|
2018-07-25 19:57:17 +00:00
|
|
|
|
|
2019-03-24 05:25:51 +00:00
|
|
|
|
if self.preview_webview is None:
|
|
|
|
|
self.preview_webview = WebKit.WebView()
|
|
|
|
|
self.preview_webview.get_settings().set_allow_universal_access_from_file_urls(True)
|
2018-06-30 00:11:47 +00:00
|
|
|
|
|
2019-03-24 05:25:51 +00:00
|
|
|
|
# Show preview once the load is finished
|
|
|
|
|
self.preview_webview.connect("load-changed", self.on_preview_load_change)
|
2014-09-01 21:07:18 +00:00
|
|
|
|
|
2019-03-24 05:25:51 +00:00
|
|
|
|
# This saying that all links will be opened in default browser, \
|
|
|
|
|
# but local files are opened in appropriate apps:
|
|
|
|
|
self.preview_webview.connect("decide-policy", self.on_click_link)
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2019-04-15 01:35:24 +00:00
|
|
|
|
self.preview_webview.load_html(output, 'file://localhost/')
|
|
|
|
|
|
|
|
|
|
def reload_preview(self):
|
|
|
|
|
if self.preview_webview:
|
|
|
|
|
self.show_preview()
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
def load_file(self, filename=None):
|
|
|
|
|
"""Open File from command line or open / open recent etc."""
|
2018-11-23 23:51:18 +00:00
|
|
|
|
if self.check_change() == Gtk.ResponseType.CANCEL:
|
|
|
|
|
return
|
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
if filename:
|
|
|
|
|
if filename.startswith('file://'):
|
|
|
|
|
filename = filename[7:]
|
|
|
|
|
filename = urllib.parse.unquote_plus(filename)
|
2019-03-31 02:16:18 +00:00
|
|
|
|
self.text_view.clear()
|
2018-07-29 18:48:45 +00:00
|
|
|
|
try:
|
2019-03-31 02:16:18 +00:00
|
|
|
|
if os.path.exists(filename):
|
2018-07-29 18:48:45 +00:00
|
|
|
|
current_file = codecs.open(filename, encoding="utf-8", mode='r')
|
2019-03-31 02:16:18 +00:00
|
|
|
|
self.text_view.set_text(current_file.read())
|
2018-07-29 18:48:45 +00:00
|
|
|
|
current_file.close()
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2019-03-31 02:16:18 +00:00
|
|
|
|
self.set_headerbar_title(os.path.basename(filename) + self.title_end)
|
2018-07-29 18:48:45 +00:00
|
|
|
|
self.set_filename(filename)
|
2014-09-11 15:44:50 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
except Exception:
|
|
|
|
|
LOGGER.warning("Error Reading File: %r" % Exception)
|
|
|
|
|
self.did_change = False
|
|
|
|
|
else:
|
|
|
|
|
LOGGER.warning("No File arg")
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
def open_uberwriter_markdown(self, _widget=None, _data=None):
|
|
|
|
|
"""open a markdown mini tutorial
|
|
|
|
|
"""
|
2018-10-14 15:46:30 +00:00
|
|
|
|
if self.check_change() == Gtk.ResponseType.CANCEL:
|
|
|
|
|
return
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
self.load_file(helpers.get_media_file('uberwriter_markdown.md'))
|
2014-08-30 08:22:01 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
def open_search_and_replace(self):
|
|
|
|
|
"""toggle the search box
|
|
|
|
|
"""
|
2014-08-08 11:25:57 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
self.searchreplace.toggle_search()
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
def open_advanced_export(self, _widget=None, _data=None):
|
|
|
|
|
"""open the export and advanced export dialog
|
|
|
|
|
"""
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2019-03-31 02:16:18 +00:00
|
|
|
|
self.export = Export(self.filename)
|
2018-07-29 18:48:45 +00:00
|
|
|
|
self.export.dialog.set_transient_for(self)
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
response = self.export.dialog.run()
|
|
|
|
|
if response == 1:
|
2019-03-31 02:16:18 +00:00
|
|
|
|
self.export.export(bytes(self.text_view.get_text(), "utf-8"))
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
self.export.dialog.destroy()
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
def open_recent(self, _widget, data=None):
|
|
|
|
|
"""open the given recent document
|
|
|
|
|
"""
|
2018-11-03 12:35:47 +00:00
|
|
|
|
print("open")
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
if data:
|
|
|
|
|
if self.check_change() == Gtk.ResponseType.CANCEL:
|
|
|
|
|
return
|
|
|
|
|
self.load_file(data)
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
def poll_for_motion(self):
|
|
|
|
|
"""check if the user has moved the cursor to show the headerbar
|
2014-08-30 08:22:01 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
Returns:
|
|
|
|
|
True -- Gtk things
|
|
|
|
|
"""
|
2018-06-28 17:47:14 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
if (self.was_motion is False
|
|
|
|
|
and self.status_bar_visible
|
|
|
|
|
and self.buffer_modified_for_status_bar
|
2019-03-31 02:16:18 +00:00
|
|
|
|
and self.text_view.props.has_focus): # pylint: disable=no-member
|
2018-07-29 18:48:45 +00:00
|
|
|
|
# self.status_bar.set_state_flags(Gtk.StateFlags.INSENSITIVE, True)
|
|
|
|
|
self.statusbar_revealer.set_reveal_child(False)
|
2018-10-14 15:46:30 +00:00
|
|
|
|
self.headerbar.hb_revealer.set_reveal_child(False)
|
2018-07-29 18:48:45 +00:00
|
|
|
|
self.status_bar_visible = False
|
|
|
|
|
self.buffer_modified_for_status_bar = False
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
self.was_motion = False
|
|
|
|
|
return True
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
def on_motion_notify(self, _widget, event, _data=None):
|
|
|
|
|
"""check the motion of the mouse to fade in the headerbar
|
|
|
|
|
"""
|
|
|
|
|
now = event.get_time()
|
|
|
|
|
if now - self.timestamp_last_mouse_motion > 150:
|
|
|
|
|
# filter out accidental motions
|
|
|
|
|
self.timestamp_last_mouse_motion = now
|
|
|
|
|
return
|
|
|
|
|
if now - self.timestamp_last_mouse_motion < 100:
|
|
|
|
|
# filter out accidental motion
|
|
|
|
|
return
|
|
|
|
|
if now - self.timestamp_last_mouse_motion > 100:
|
|
|
|
|
# react on motion by fading in headerbar and statusbar
|
|
|
|
|
if self.status_bar_visible is False:
|
|
|
|
|
self.statusbar_revealer.set_reveal_child(True)
|
2018-10-14 15:46:30 +00:00
|
|
|
|
self.headerbar.hb_revealer.set_reveal_child(True)
|
|
|
|
|
self.headerbar.hb.props.opacity = 1
|
2018-07-29 18:48:45 +00:00
|
|
|
|
self.status_bar_visible = True
|
|
|
|
|
self.buffer_modified_for_status_bar = False
|
|
|
|
|
self.update_line_and_char_count()
|
|
|
|
|
# self.status_bar.set_state_flags(Gtk.StateFlags.NORMAL, True)
|
|
|
|
|
self.was_motion = True
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
def focus_out(self, _widget, _data=None):
|
|
|
|
|
"""events called when the window losses focus
|
|
|
|
|
"""
|
|
|
|
|
if self.status_bar_visible is False:
|
|
|
|
|
self.statusbar_revealer.set_reveal_child(True)
|
2018-10-14 15:46:30 +00:00
|
|
|
|
self.headerbar.hb_revealer.set_reveal_child(True)
|
|
|
|
|
self.headerbar.hb.props.opacity = 1
|
2018-07-29 18:48:45 +00:00
|
|
|
|
self.status_bar_visible = True
|
|
|
|
|
self.buffer_modified_for_status_bar = False
|
|
|
|
|
self.update_line_and_char_count()
|
2014-10-02 17:02:59 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
def draw_gradient(self, _widget, cr):
|
|
|
|
|
"""draw fading gradient over the top and the bottom of the
|
|
|
|
|
TextWindow
|
|
|
|
|
"""
|
|
|
|
|
bg_color = self.get_style_context().get_background_color(Gtk.StateFlags.ACTIVE)
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2019-01-27 19:20:56 +00:00
|
|
|
|
lg_top = cairo.LinearGradient(0, 0, 0, 35) # pylint: disable=no-member
|
2018-07-29 18:48:45 +00:00
|
|
|
|
lg_top.add_color_stop_rgba(
|
|
|
|
|
0, bg_color.red, bg_color.green, bg_color.blue, 1)
|
|
|
|
|
lg_top.add_color_stop_rgba(
|
|
|
|
|
1, bg_color.red, bg_color.green, bg_color.blue, 0)
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
width = self.scrolled_window.get_allocation().width
|
|
|
|
|
height = self.scrolled_window.get_allocation().height
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
cr.rectangle(0, 0, width, 35)
|
|
|
|
|
cr.set_source(lg_top)
|
|
|
|
|
cr.fill()
|
|
|
|
|
cr.rectangle(0, height - 35, width, height)
|
2014-08-30 08:22:01 +00:00
|
|
|
|
|
2019-01-27 19:20:56 +00:00
|
|
|
|
lg_btm = cairo.LinearGradient(0, height - 35, 0, height) # pylint: disable=no-member
|
2018-07-29 18:48:45 +00:00
|
|
|
|
lg_btm.add_color_stop_rgba(
|
|
|
|
|
1, bg_color.red, bg_color.green, bg_color.blue, 1)
|
|
|
|
|
lg_btm.add_color_stop_rgba(
|
|
|
|
|
0, bg_color.red, bg_color.green, bg_color.blue, 0)
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2018-07-29 18:48:45 +00:00
|
|
|
|
cr.set_source(lg_btm)
|
|
|
|
|
cr.fill()
|
|
|
|
|
|
2018-07-25 19:57:17 +00:00
|
|
|
|
def on_delete_called(self, _widget, _data=None):
|
|
|
|
|
"""Called when the TexteditorWindow is closed.
|
|
|
|
|
"""
|
|
|
|
|
LOGGER.info('delete called')
|
2014-07-06 20:35:24 +00:00
|
|
|
|
if self.check_change() == Gtk.ResponseType.CANCEL:
|
|
|
|
|
return True
|
|
|
|
|
return False
|
|
|
|
|
|
2018-07-25 19:57:17 +00:00
|
|
|
|
def on_mnu_close_activate(self, _widget, _data=None):
|
2019-04-15 02:01:59 +00:00
|
|
|
|
"""Signal handler for closing the Window.
|
2018-07-25 19:57:17 +00:00
|
|
|
|
Overriden from parent Window Class
|
2014-07-06 20:35:24 +00:00
|
|
|
|
"""
|
|
|
|
|
if self.on_delete_called(self): # Really destroy?
|
|
|
|
|
return
|
2018-07-25 19:57:17 +00:00
|
|
|
|
self.destroy()
|
2014-07-06 20:35:24 +00:00
|
|
|
|
return
|
|
|
|
|
|
2018-07-25 19:57:17 +00:00
|
|
|
|
def on_destroy(self, _widget, _data=None):
|
|
|
|
|
"""Called when the TexteditorWindow is closed.
|
|
|
|
|
"""
|
2014-07-06 20:35:24 +00:00
|
|
|
|
# Clean up code for saving application state should be added here.
|
|
|
|
|
Gtk.main_quit()
|
|
|
|
|
|
2014-08-08 11:25:57 +00:00
|
|
|
|
def set_headerbar_title(self, title):
|
2018-07-25 19:57:17 +00:00
|
|
|
|
"""set the desired headerbar title
|
|
|
|
|
"""
|
2018-10-14 15:46:30 +00:00
|
|
|
|
self.headerbar.hb.props.title = title
|
|
|
|
|
self.fs_headerbar.hb.props.title = title
|
2014-08-30 08:22:01 +00:00
|
|
|
|
self.set_title(title)
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2018-05-06 18:00:14 +00:00
|
|
|
|
def set_filename(self, filename=None):
|
2018-07-25 19:57:17 +00:00
|
|
|
|
"""set filename
|
|
|
|
|
"""
|
2018-05-06 18:00:14 +00:00
|
|
|
|
if filename:
|
|
|
|
|
self.filename = filename
|
|
|
|
|
base_path = os.path.dirname(self.filename)
|
|
|
|
|
else:
|
|
|
|
|
self.filename = None
|
|
|
|
|
base_path = "/"
|
|
|
|
|
self.settings.set_value("open-file-path", GLib.Variant("s", base_path))
|
2018-06-28 17:47:14 +00:00
|
|
|
|
|
2019-03-23 17:05:02 +00:00
|
|
|
|
def on_preview_load_change(self, webview, event):
|
|
|
|
|
"""swaps text editor with preview once the load is complete
|
|
|
|
|
"""
|
|
|
|
|
if event == WebKit.LoadEvent.FINISHED:
|
2019-03-24 05:25:51 +00:00
|
|
|
|
self.show_preview(loaded=True)
|
2019-03-23 17:05:02 +00:00
|
|
|
|
|
2018-07-25 19:57:17 +00:00
|
|
|
|
def on_click_link(self, web_view, decision, _decision_type):
|
|
|
|
|
"""provide ability for self.webview to open links in default browser
|
|
|
|
|
"""
|
|
|
|
|
if web_view.get_uri().startswith(("http://", "https://", "www.")):
|
|
|
|
|
webbrowser.open(web_view.get_uri())
|
|
|
|
|
decision.ignore()
|
|
|
|
|
return True # Don't let the event "bubble up"
|