2014-07-06 20:35:24 +00:00
|
|
|
|
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
|
|
|
|
|
### BEGIN LICENSE
|
|
|
|
|
# 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/>.
|
|
|
|
|
### END LICENSE
|
|
|
|
|
|
|
|
|
|
import locale
|
|
|
|
|
import subprocess
|
|
|
|
|
import os
|
|
|
|
|
import codecs
|
|
|
|
|
import webbrowser
|
|
|
|
|
import urllib
|
|
|
|
|
import pickle
|
|
|
|
|
|
|
|
|
|
from locale import gettext as _
|
|
|
|
|
locale.textdomain('uberwriter')
|
|
|
|
|
|
|
|
|
|
import mimetypes
|
|
|
|
|
|
2014-08-08 11:25:57 +00:00
|
|
|
|
from gi.repository import Gtk, Gdk, GObject, WebKit, Gio # pylint: disable=E0611
|
2014-07-06 20:35:24 +00:00
|
|
|
|
from gi.repository import Pango # pylint: disable=E0611
|
|
|
|
|
|
|
|
|
|
import cairo
|
2014-08-30 08:22:01 +00:00
|
|
|
|
# import cairo.Pattern, cairo.SolidPattern
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
|
|
|
|
import re
|
|
|
|
|
|
|
|
|
|
from .MarkupBuffer import MarkupBuffer
|
|
|
|
|
from .UberwriterTextEditor import TextEditor
|
|
|
|
|
from .UberwriterInlinePreview import UberwriterInlinePreview
|
|
|
|
|
from .UberwriterSidebar import UberwriterSidebar
|
|
|
|
|
from .UberwriterSearchAndReplace import UberwriterSearchAndReplace
|
2014-10-02 17:02:59 +00:00
|
|
|
|
from .UberwriterAutoCorrect import UberwriterAutoCorrect
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
|
|
|
|
import logging
|
|
|
|
|
logger = logging.getLogger('uberwriter')
|
|
|
|
|
|
|
|
|
|
# Spellcheck
|
|
|
|
|
|
|
|
|
|
import locale
|
2014-08-08 11:25:57 +00:00
|
|
|
|
|
2014-10-02 17:02:59 +00:00
|
|
|
|
from uberwriter_lib.gtkspellcheck import SpellChecker
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
import apt
|
|
|
|
|
APT_ENABLED = True
|
|
|
|
|
except:
|
|
|
|
|
APT_ENABLED = False
|
|
|
|
|
|
|
|
|
|
from uberwriter_lib import Window
|
|
|
|
|
from uberwriter_lib import helpers
|
|
|
|
|
from .AboutUberwriterDialog import AboutUberwriterDialog
|
|
|
|
|
from .UberwriterAdvancedExportDialog import UberwriterAdvancedExportDialog
|
2016-02-05 22:52:43 +00:00
|
|
|
|
from .plugins.bibtex import BibTex
|
2014-07-06 20:35:24 +00:00
|
|
|
|
# Some Globals
|
|
|
|
|
# TODO move them somewhere for better
|
|
|
|
|
# accesibility from other files
|
|
|
|
|
|
|
|
|
|
CONFIG_PATH = os.path.expanduser("~/.config/uberwriter/")
|
|
|
|
|
|
|
|
|
|
# See texteditor_lib.Window.py for more details about how this class works
|
|
|
|
|
class UberwriterWindow(Window):
|
|
|
|
|
|
|
|
|
|
__gtype_name__ = "UberwriterWindow"
|
|
|
|
|
|
2014-08-30 08:22:01 +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-focusmode': (GObject.SIGNAL_ACTION, None, ()),
|
2016-02-05 22:52:43 +00:00
|
|
|
|
'toggle-bibtex': (GObject.SIGNAL_ACTION, None, ()),
|
2014-08-30 08:22:01 +00:00
|
|
|
|
'toggle-fullscreen': (GObject.SIGNAL_ACTION, None, ()),
|
|
|
|
|
'toggle-spellcheck': (GObject.SIGNAL_ACTION, None, ()),
|
|
|
|
|
'toggle-preview': (GObject.SIGNAL_ACTION, None, ()),
|
2014-09-01 21:07:18 +00:00
|
|
|
|
'toggle-search': (GObject.SIGNAL_ACTION, None, ()),
|
|
|
|
|
'toggle-search-replace': (GObject.SIGNAL_ACTION, None, ()),
|
2014-08-30 08:22:01 +00:00
|
|
|
|
'close-window': (GObject.SIGNAL_ACTION, None, ())
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-06 20:35:24 +00:00
|
|
|
|
def scrolled(self, widget):
|
|
|
|
|
"""if window scrolled + focusmode make font black again"""
|
2014-08-30 08:22:01 +00:00
|
|
|
|
# if self.focusmode:
|
|
|
|
|
# if self.textchange == False:
|
|
|
|
|
# if self.scroll_count >= 4:
|
|
|
|
|
# self.TextBuffer.apply_tag(
|
|
|
|
|
# self.MarkupBuffer.blackfont,
|
|
|
|
|
# self.TextBuffer.get_start_iter(),
|
|
|
|
|
# self.TextBuffer.get_end_iter())
|
|
|
|
|
# else:
|
|
|
|
|
# self.scroll_count += 1
|
|
|
|
|
# else:
|
|
|
|
|
# self.scroll_count = 0
|
|
|
|
|
# self.textchange = False
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
|
|
|
|
def paste_done(self, *args):
|
|
|
|
|
self.MarkupBuffer.markup_buffer(0)
|
|
|
|
|
|
|
|
|
|
def init_typewriter(self):
|
2014-10-02 17:02:59 +00:00
|
|
|
|
self.EditorAlignment.props.margin_top = self.window_height / 2
|
|
|
|
|
self.EditorAlignment.props.margin_bottom = self.window_height / 2
|
2014-07-06 20:35:24 +00:00
|
|
|
|
self.typewriter_initiated = True
|
|
|
|
|
|
|
|
|
|
def remove_typewriter(self):
|
2014-10-02 17:02:59 +00:00
|
|
|
|
self.EditorAlignment.props.margin_top = self.alignment_padding
|
|
|
|
|
self.EditorAlignment.props.margin_bottom = 250
|
2014-07-06 20:35:24 +00:00
|
|
|
|
self.text_change_event = self.TextBuffer.connect('changed', self.text_changed)
|
|
|
|
|
|
2014-08-30 08:22:01 +00:00
|
|
|
|
def get_text(self):
|
|
|
|
|
start_iter = self.TextBuffer.get_start_iter()
|
|
|
|
|
end_iter = self.TextBuffer.get_end_iter()
|
|
|
|
|
return self.TextBuffer.get_text(start_iter, end_iter, False)
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2014-09-11 16:37:50 +00:00
|
|
|
|
WORDCOUNT = re.compile(r"(?!\-\w)[\s#*\+\-]+", re.UNICODE)
|
2014-07-06 20:35:24 +00:00
|
|
|
|
def update_line_and_char_count(self):
|
|
|
|
|
if self.status_bar_visible == False:
|
|
|
|
|
return
|
2014-08-30 08:22:01 +00:00
|
|
|
|
self.char_count.set_text(str(self.TextBuffer.get_char_count()))
|
2014-07-06 20:35:24 +00:00
|
|
|
|
text = self.get_text()
|
|
|
|
|
words = re.split(self.WORDCOUNT, text)
|
|
|
|
|
length = len(words)
|
|
|
|
|
# Last word a "space"
|
|
|
|
|
if len(words[-1]) == 0:
|
|
|
|
|
length = length - 1
|
|
|
|
|
# First word a "space" (happens in focus mode...)
|
|
|
|
|
if len(words[0]) == 0:
|
|
|
|
|
length = length - 1
|
|
|
|
|
if length == -1:
|
|
|
|
|
length = 0
|
|
|
|
|
self.word_count.set_text(str(length))
|
|
|
|
|
|
|
|
|
|
def mark_set(self, buffer, location, mark, data=None):
|
2014-09-01 21:07:18 +00:00
|
|
|
|
if mark.get_name() in ['insert', 'gtk_drag_target']:
|
|
|
|
|
self.check_scroll(mark)
|
|
|
|
|
return True
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
|
|
|
|
def text_changed(self, widget, data=None):
|
|
|
|
|
if self.did_change == False:
|
|
|
|
|
self.did_change = True
|
|
|
|
|
title = self.get_title()
|
2014-08-08 11:25:57 +00:00
|
|
|
|
self.set_headerbar_title("* " + title)
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
|
|
|
|
self.MarkupBuffer.markup_buffer(1)
|
|
|
|
|
self.textchange = True
|
|
|
|
|
|
|
|
|
|
self.buffer_modified_for_status_bar = True
|
|
|
|
|
self.update_line_and_char_count()
|
2014-09-01 21:07:18 +00:00
|
|
|
|
self.check_scroll(self.TextBuffer.get_insert())
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
|
|
|
|
def toggle_fullscreen(self, widget, data=None):
|
|
|
|
|
if widget.get_active():
|
|
|
|
|
self.fullscreen()
|
|
|
|
|
key, mod = Gtk.accelerator_parse("Escape")
|
|
|
|
|
self.fullscreen_button.add_accelerator("activate",
|
2014-08-30 08:22:01 +00:00
|
|
|
|
self.accel_group, key, mod, Gtk.AccelFlags.VISIBLE)
|
2014-07-06 20:35:24 +00:00
|
|
|
|
# Hide Menu
|
|
|
|
|
self.menubar.hide()
|
|
|
|
|
else:
|
|
|
|
|
self.unfullscreen()
|
|
|
|
|
key, mod = Gtk.accelerator_parse("Escape")
|
|
|
|
|
self.fullscreen_button.remove_accelerator(
|
|
|
|
|
self.accel_group, key, mod)
|
2015-02-12 18:09:46 +00:00
|
|
|
|
self.menubar.hide()
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
|
|
|
|
self.TextEditor.grab_focus()
|
|
|
|
|
|
|
|
|
|
def set_focusmode(self, widget, data=None):
|
|
|
|
|
if widget.get_active():
|
|
|
|
|
self.init_typewriter()
|
|
|
|
|
self.MarkupBuffer.focusmode_highlight()
|
|
|
|
|
self.focusmode = True
|
|
|
|
|
self.TextEditor.grab_focus()
|
2014-09-01 21:07:18 +00:00
|
|
|
|
self.check_scroll(self.TextBuffer.get_insert())
|
2014-07-06 20:35:24 +00:00
|
|
|
|
if self.spellcheck != False:
|
|
|
|
|
self.SpellChecker._misspelled.set_property('underline', 0)
|
|
|
|
|
else:
|
|
|
|
|
self.remove_typewriter()
|
|
|
|
|
self.focusmode = False
|
|
|
|
|
self.TextBuffer.remove_tag(self.MarkupBuffer.grayfont,
|
|
|
|
|
self.TextBuffer.get_start_iter(),
|
|
|
|
|
self.TextBuffer.get_end_iter())
|
|
|
|
|
self.TextBuffer.remove_tag(self.MarkupBuffer.blackfont,
|
|
|
|
|
self.TextBuffer.get_start_iter(),
|
|
|
|
|
self.TextBuffer.get_end_iter())
|
|
|
|
|
|
|
|
|
|
self.MarkupBuffer.markup_buffer(1)
|
|
|
|
|
self.TextEditor.grab_focus()
|
|
|
|
|
self.update_line_and_char_count()
|
2014-08-30 08:22:01 +00:00
|
|
|
|
self.check_scroll()
|
2014-07-06 20:35:24 +00:00
|
|
|
|
if self.spellcheck != False:
|
|
|
|
|
self.SpellChecker._misspelled.set_property('underline', 4)
|
|
|
|
|
|
2014-08-30 08:22:01 +00:00
|
|
|
|
def scroll_smoothly(self, widget, frame_clock, data = None):
|
|
|
|
|
if self.smooth_scroll_data['target_pos'] == -1:
|
|
|
|
|
return True
|
|
|
|
|
def ease_out_cubic(t):
|
|
|
|
|
p = t - 1;
|
|
|
|
|
return p * p * p + 1;
|
2014-10-02 17:02:59 +00:00
|
|
|
|
|
2014-08-30 08:22:01 +00:00
|
|
|
|
now = frame_clock.get_frame_time()
|
|
|
|
|
if self.smooth_scroll_acttarget != self.smooth_scroll_data['target_pos']:
|
|
|
|
|
self.smooth_scroll_starttime = now
|
|
|
|
|
self.smooth_scroll_endtime = now + self.smooth_scroll_data['duration'] * 100
|
|
|
|
|
self.smooth_scroll_acttarget = self.smooth_scroll_data['target_pos']
|
|
|
|
|
|
|
|
|
|
if(now < self.smooth_scroll_endtime):
|
|
|
|
|
t = float(now - self.smooth_scroll_starttime) / float(self.smooth_scroll_endtime - self.smooth_scroll_starttime)
|
|
|
|
|
else:
|
|
|
|
|
t = 1
|
2014-10-02 17:02:59 +00:00
|
|
|
|
pos = self.smooth_scroll_data['source_pos'] + (t * (self.smooth_scroll_data['target_pos'] - self.smooth_scroll_data['source_pos']))
|
|
|
|
|
widget.get_vadjustment().props.value = pos
|
|
|
|
|
self.smooth_scroll_data['target_pos'] = -1
|
|
|
|
|
return True
|
|
|
|
|
|
2014-08-30 08:22:01 +00:00
|
|
|
|
t = ease_out_cubic(t)
|
|
|
|
|
pos = self.smooth_scroll_data['source_pos'] + (t * (self.smooth_scroll_data['target_pos'] - self.smooth_scroll_data['source_pos']))
|
|
|
|
|
widget.get_vadjustment().props.value = pos
|
|
|
|
|
return True # continue ticking
|
|
|
|
|
|
2014-10-05 07:51:10 +00:00
|
|
|
|
def check_scroll(self, mark=None):
|
2014-08-30 08:22:01 +00:00
|
|
|
|
gradient_offset = 80
|
|
|
|
|
buf = self.TextEditor.get_buffer()
|
2014-10-05 07:51:10 +00:00
|
|
|
|
if mark:
|
|
|
|
|
ins_it = buf.get_iter_at_mark(mark)
|
|
|
|
|
else:
|
|
|
|
|
ins_it = buf.get_iter_at_mark(buf.get_insert())
|
2014-08-30 08:22:01 +00:00
|
|
|
|
loc_rect = self.TextEditor.get_iter_location(ins_it)
|
|
|
|
|
|
|
|
|
|
# alignment offset added from top
|
2014-10-02 17:02:59 +00:00
|
|
|
|
pos_y = loc_rect.y + loc_rect.height + self.EditorAlignment.props.margin_top
|
2014-08-30 08:22:01 +00:00
|
|
|
|
|
|
|
|
|
ha = self.ScrolledWindow.get_vadjustment()
|
|
|
|
|
if ha.props.page_size < gradient_offset:
|
|
|
|
|
return
|
|
|
|
|
pos = pos_y - ha.props.value
|
|
|
|
|
# print("pos: %i, pos_y %i, page_sz: %i, val: %i" % (pos, pos_y, ha.props.page_size - gradient_offset, ha.props.value))
|
|
|
|
|
# global t, amount, initvadjustment
|
|
|
|
|
target_pos = -1
|
|
|
|
|
if self.focusmode:
|
2014-09-11 15:44:50 +00:00
|
|
|
|
# print("pos: %i > %i" % (pos, ha.props.page_size * 0.5))
|
2014-08-30 08:22:01 +00:00
|
|
|
|
if pos != (ha.props.page_size * 0.5):
|
|
|
|
|
target_pos = pos_y - (ha.props.page_size * 0.5)
|
2014-09-11 15:44:50 +00:00
|
|
|
|
elif pos > ha.props.page_size - gradient_offset - 60:
|
2014-10-02 17:02:59 +00:00
|
|
|
|
target_pos = pos_y - ha.props.page_size + gradient_offset + 40
|
2014-08-30 08:22:01 +00:00
|
|
|
|
elif pos < gradient_offset:
|
|
|
|
|
target_pos = pos_y - gradient_offset
|
|
|
|
|
self.smooth_scroll_data = {
|
|
|
|
|
'target_pos': target_pos,
|
|
|
|
|
'source_pos': ha.props.value,
|
|
|
|
|
'duration': 2000
|
|
|
|
|
}
|
|
|
|
|
if self.smooth_scroll_tickid == -1:
|
|
|
|
|
self.smooth_scroll_tickid = self.ScrolledWindow.add_tick_callback(self.scroll_smoothly)
|
|
|
|
|
|
2014-07-06 20:35:24 +00:00
|
|
|
|
def window_resize(self, widget, data=None):
|
|
|
|
|
# To calc padding top / bottom
|
|
|
|
|
self.window_height = widget.get_size()[1]
|
|
|
|
|
w_width = widget.get_size()[0]
|
|
|
|
|
# Calculate left / right margin
|
2014-08-30 08:22:01 +00:00
|
|
|
|
width_request = 600
|
2014-07-06 20:35:24 +00:00
|
|
|
|
if(w_width < 900):
|
2014-09-11 15:44:50 +00:00
|
|
|
|
# self.MarkupBuffer.set_multiplier(8)
|
2014-08-30 08:22:01 +00:00
|
|
|
|
self.current_font_size = 12
|
|
|
|
|
self.alignment_padding = 30
|
|
|
|
|
lm = 7 * 8
|
2014-09-01 21:07:18 +00:00
|
|
|
|
self.get_style_context().remove_class("medium")
|
|
|
|
|
self.get_style_context().remove_class("large")
|
|
|
|
|
self.get_style_context().add_class("small")
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
|
|
|
|
elif(w_width < 1400):
|
2014-09-11 15:44:50 +00:00
|
|
|
|
# self.MarkupBuffer.set_multiplier(10)
|
2014-08-30 08:22:01 +00:00
|
|
|
|
width_request = 800
|
|
|
|
|
self.current_font_size = 15
|
|
|
|
|
self.alignment_padding = 40
|
|
|
|
|
lm = 7 * 10
|
2014-09-01 21:07:18 +00:00
|
|
|
|
self.get_style_context().remove_class("small")
|
|
|
|
|
self.get_style_context().remove_class("large")
|
|
|
|
|
self.get_style_context().add_class("medium")
|
|
|
|
|
|
2014-07-06 20:35:24 +00:00
|
|
|
|
else:
|
2014-09-11 15:44:50 +00:00
|
|
|
|
# self.MarkupBuffer.set_multiplier(13)
|
2014-08-30 08:22:01 +00:00
|
|
|
|
self.current_font_size = 17
|
|
|
|
|
width_request = 1000
|
|
|
|
|
self.alignment_padding = 60
|
|
|
|
|
lm = 7 * 13
|
2014-09-01 21:07:18 +00:00
|
|
|
|
self.get_style_context().remove_class("medium")
|
|
|
|
|
self.get_style_context().remove_class("small")
|
|
|
|
|
self.get_style_context().add_class("large")
|
|
|
|
|
|
2014-09-11 15:44:50 +00:00
|
|
|
|
|
2014-10-02 17:02:59 +00:00
|
|
|
|
self.EditorAlignment.props.margin_bottom = self.alignment_padding + 30
|
|
|
|
|
self.EditorAlignment.props.margin_top = self.alignment_padding
|
2014-07-06 20:35:24 +00:00
|
|
|
|
self.TextEditor.set_left_margin(lm)
|
|
|
|
|
self.TextEditor.set_right_margin(lm)
|
|
|
|
|
|
|
|
|
|
self.MarkupBuffer.recalculate(lm)
|
|
|
|
|
|
|
|
|
|
if self.focusmode:
|
|
|
|
|
self.remove_typewriter()
|
|
|
|
|
self.init_typewriter()
|
|
|
|
|
|
2014-08-30 08:22:01 +00:00
|
|
|
|
if self.TextEditor.props.width_request != width_request:
|
|
|
|
|
self.TextEditor.props.width_request = width_request
|
|
|
|
|
alloc = self.TextEditor.get_allocation()
|
|
|
|
|
alloc.width = width_request
|
|
|
|
|
self.TextEditor.size_allocate(alloc)
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2014-09-11 15:44:50 +00:00
|
|
|
|
def style_changed(self, widget, data=None):
|
|
|
|
|
pgc = self.TextEditor.get_pango_context()
|
|
|
|
|
mets = pgc.get_metrics()
|
|
|
|
|
self.MarkupBuffer.set_multiplier(Pango.units_to_double(mets.get_approximate_char_width()) + 1)
|
|
|
|
|
|
|
|
|
|
|
2014-07-06 20:35:24 +00:00
|
|
|
|
def save_document(self, widget, data=None):
|
|
|
|
|
if self.filename:
|
|
|
|
|
logger.info("saving")
|
|
|
|
|
filename = self.filename
|
|
|
|
|
f = codecs.open(filename, encoding="utf-8", mode='w')
|
|
|
|
|
f.write(self.get_text())
|
|
|
|
|
f.close()
|
|
|
|
|
if self.did_change:
|
|
|
|
|
self.did_change = False
|
|
|
|
|
title = self.get_title()
|
2014-08-08 11:25:57 +00:00
|
|
|
|
self.set_headerbar_title(title[2:])
|
2014-07-06 20:35:24 +00:00
|
|
|
|
return Gtk.ResponseType.OK
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
|
|
filefilter = Gtk.FileFilter.new()
|
|
|
|
|
filefilter.add_mime_type('text/x-markdown')
|
|
|
|
|
filefilter.add_mime_type('text/plain')
|
|
|
|
|
filefilter.set_name('MarkDown (.md)')
|
|
|
|
|
filechooser = Gtk.FileChooserDialog(
|
|
|
|
|
_("Save your File"),
|
|
|
|
|
self,
|
|
|
|
|
Gtk.FileChooserAction.SAVE,
|
2014-10-02 17:02:59 +00:00
|
|
|
|
("_Cancel", Gtk.ResponseType.CANCEL,
|
|
|
|
|
"_Save", Gtk.ResponseType.OK)
|
2014-07-06 20:35:24 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
filechooser.set_do_overwrite_confirmation(True)
|
|
|
|
|
filechooser.add_filter(filefilter)
|
|
|
|
|
response = filechooser.run()
|
|
|
|
|
if response == Gtk.ResponseType.OK:
|
|
|
|
|
filename = filechooser.get_filename()
|
|
|
|
|
|
|
|
|
|
if filename[-3:] != ".md":
|
|
|
|
|
filename = filename + ".md"
|
|
|
|
|
try:
|
|
|
|
|
self.recent_manager.add_item("file:/ " + filename)
|
|
|
|
|
except:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
f = codecs.open(filename, encoding="utf-8", mode='w')
|
|
|
|
|
|
|
|
|
|
f.write(self.get_text())
|
|
|
|
|
f.close()
|
|
|
|
|
|
|
|
|
|
self.filename = filename
|
2014-08-08 11:25:57 +00:00
|
|
|
|
self.set_headerbar_title(os.path.basename(filename) + self.title_end)
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
|
|
|
|
self.did_change = False
|
|
|
|
|
filechooser.destroy()
|
|
|
|
|
return response
|
|
|
|
|
|
2014-09-01 21:07:18 +00:00
|
|
|
|
else:
|
2014-07-06 20:35:24 +00:00
|
|
|
|
filechooser.destroy()
|
2014-09-01 21:07:18 +00:00
|
|
|
|
return Gtk.ResponseType.CANCEL
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
|
|
|
|
def save_document_as(self, widget, data=None):
|
|
|
|
|
filechooser = Gtk.FileChooserDialog(
|
|
|
|
|
"Save your File",
|
|
|
|
|
self,
|
|
|
|
|
Gtk.FileChooserAction.SAVE,
|
2014-10-02 17:02:59 +00:00
|
|
|
|
("_Cancel", Gtk.ResponseType.CANCEL,
|
|
|
|
|
"_Save", Gtk.ResponseType.OK)
|
2014-07-06 20:35:24 +00:00
|
|
|
|
)
|
|
|
|
|
filechooser.set_do_overwrite_confirmation(True)
|
|
|
|
|
if self.filename:
|
|
|
|
|
filechooser.set_filename(self.filename)
|
|
|
|
|
response = filechooser.run()
|
|
|
|
|
if response == Gtk.ResponseType.OK:
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
f = codecs.open(filename, encoding="utf-8", mode='w')
|
|
|
|
|
f.write(self.get_text())
|
|
|
|
|
f.close()
|
|
|
|
|
|
|
|
|
|
self.filename = filename
|
2014-08-08 11:25:57 +00:00
|
|
|
|
self.set_headerbar_title(os.path.basename(filename) + self.title_end)
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
self.recent_manager.add_item(filename)
|
|
|
|
|
except:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
filechooser.destroy()
|
|
|
|
|
self.did_change = False
|
|
|
|
|
|
2014-09-01 21:07:18 +00:00
|
|
|
|
else:
|
2014-07-06 20:35:24 +00:00
|
|
|
|
filechooser.destroy()
|
2014-09-01 21:07:18 +00:00
|
|
|
|
return Gtk.ResponseType.CANCEL
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
|
|
|
|
def export(self, export_type="html"):
|
|
|
|
|
filechooser = Gtk.FileChooserDialog(
|
|
|
|
|
"Export as %s" % export_type.upper(),
|
|
|
|
|
self,
|
|
|
|
|
Gtk.FileChooserAction.SAVE,
|
2014-10-02 17:02:59 +00:00
|
|
|
|
("_Cancel", Gtk.ResponseType.CANCEL,
|
|
|
|
|
"_Save", Gtk.ResponseType.OK)
|
2014-07-06 20:35:24 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
filechooser.set_do_overwrite_confirmation(True)
|
|
|
|
|
if self.filename:
|
|
|
|
|
filechooser.set_filename(self.filename[:-2] + export_type.lower())
|
|
|
|
|
|
|
|
|
|
response = filechooser.run()
|
|
|
|
|
if response == Gtk.ResponseType.OK:
|
|
|
|
|
filename = filechooser.get_filename()
|
|
|
|
|
if filename.endswith("." + export_type):
|
|
|
|
|
filename = filename[:-len(export_type)-1]
|
|
|
|
|
filechooser.destroy()
|
|
|
|
|
else:
|
|
|
|
|
filechooser.destroy()
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# Converting text to bytes for python 3
|
|
|
|
|
text = bytes(self.get_text(), "utf-8")
|
|
|
|
|
|
|
|
|
|
output_dir = os.path.abspath(os.path.join(filename, os.path.pardir))
|
|
|
|
|
|
|
|
|
|
basename = os.path.basename(filename)
|
|
|
|
|
|
|
|
|
|
args = ['pandoc', '--from=markdown', '--smart']
|
|
|
|
|
|
|
|
|
|
if export_type == "pdf":
|
|
|
|
|
args.append("-o%s.pdf" % basename)
|
|
|
|
|
|
|
|
|
|
elif export_type == "odt":
|
|
|
|
|
args.append("-o%s.odt" % basename)
|
|
|
|
|
|
|
|
|
|
elif export_type == "html":
|
|
|
|
|
css = helpers.get_media_file('uberwriter.css')
|
|
|
|
|
args.append("-c%s" % css)
|
|
|
|
|
args.append("-o%s.html" % basename)
|
|
|
|
|
args.append("--mathjax")
|
|
|
|
|
|
|
|
|
|
p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, cwd=output_dir)
|
|
|
|
|
output = p.communicate(text)[0]
|
|
|
|
|
|
|
|
|
|
return filename
|
|
|
|
|
|
|
|
|
|
def export_as_odt(self, widget, data=None):
|
|
|
|
|
self.export("odt")
|
|
|
|
|
|
|
|
|
|
def export_as_html(self, widget, data=None):
|
|
|
|
|
self.export("html")
|
|
|
|
|
|
|
|
|
|
def export_as_pdf(self, widget, data=None):
|
|
|
|
|
if self.texlive_installed == False and APT_ENABLED:
|
|
|
|
|
try:
|
|
|
|
|
cache = apt.Cache()
|
|
|
|
|
inst = cache["texlive"].is_installed
|
|
|
|
|
except:
|
|
|
|
|
inst = True
|
|
|
|
|
|
|
|
|
|
if inst == False:
|
|
|
|
|
dialog = Gtk.MessageDialog(self,
|
|
|
|
|
Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
|
|
|
|
|
Gtk.MessageType.INFO,
|
2014-08-08 11:25:57 +00:00
|
|
|
|
Gtk.ButtonsType.NONE,
|
2014-07-06 20:35:24 +00:00
|
|
|
|
_("You can not export to PDF.")
|
|
|
|
|
)
|
|
|
|
|
dialog.format_secondary_markup(_("Please install <a href=\"apt:texlive\">texlive</a> from the software center."))
|
|
|
|
|
response = dialog.run()
|
|
|
|
|
return
|
|
|
|
|
else:
|
|
|
|
|
self.texlive_installed = True
|
|
|
|
|
self.export("pdf")
|
|
|
|
|
|
|
|
|
|
def copy_html_to_clipboard(self, widget, date=None):
|
|
|
|
|
"""Copies only html without headers etc. to Clipboard"""
|
|
|
|
|
|
|
|
|
|
args = ['pandoc', '--from=markdown', '--smart', '-thtml']
|
|
|
|
|
p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
|
|
|
|
|
|
|
|
|
text = bytes(self.get_text(), "utf-8")
|
|
|
|
|
output = p.communicate(text)[0]
|
|
|
|
|
|
|
|
|
|
cb = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
|
|
|
|
|
cb.set_text(output.decode("utf-8"), -1)
|
|
|
|
|
cb.store()
|
|
|
|
|
|
|
|
|
|
def open_document(self, widget):
|
|
|
|
|
if self.check_change() == Gtk.ResponseType.CANCEL:
|
|
|
|
|
return
|
|
|
|
|
|
2014-08-08 11:25:57 +00:00
|
|
|
|
if self.focusmode:
|
|
|
|
|
self.focusmode_button.set_active(False)
|
|
|
|
|
|
2014-07-06 20:35:24 +00:00
|
|
|
|
filefilter = Gtk.FileFilter.new()
|
|
|
|
|
filefilter.add_mime_type('text/x-markdown')
|
|
|
|
|
filefilter.add_mime_type('text/plain')
|
|
|
|
|
filefilter.set_name(_('MarkDown or Plain Text'))
|
|
|
|
|
|
|
|
|
|
filechooser = Gtk.FileChooserDialog(
|
|
|
|
|
_("Open a .md-File"),
|
|
|
|
|
self,
|
|
|
|
|
Gtk.FileChooserAction.OPEN,
|
2014-10-02 17:02:59 +00:00
|
|
|
|
("_Cancel", Gtk.ResponseType.CANCEL,
|
|
|
|
|
"_Open", Gtk.ResponseType.OK)
|
2014-07-06 20:35:24 +00:00
|
|
|
|
)
|
|
|
|
|
filechooser.add_filter(filefilter)
|
|
|
|
|
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):
|
|
|
|
|
if self.did_change and len(self.get_text()):
|
|
|
|
|
dialog = Gtk.MessageDialog(self,
|
|
|
|
|
Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
|
|
|
|
|
Gtk.MessageType.WARNING,
|
2014-08-08 11:25:57 +00:00
|
|
|
|
Gtk.ButtonsType.NONE,
|
2014-07-06 20:35:24 +00:00
|
|
|
|
_("You have not saved your changes.")
|
|
|
|
|
)
|
|
|
|
|
dialog.add_button(_("Close without Saving"), Gtk.ResponseType.NO)
|
|
|
|
|
dialog.add_button(_("Cancel"), Gtk.ResponseType.CANCEL)
|
2014-09-01 21:07:18 +00:00
|
|
|
|
dialog.add_button(_("Save now"), Gtk.ResponseType.YES)
|
2014-07-06 20:35:24 +00:00
|
|
|
|
dialog.set_title(_('Unsaved changes'))
|
|
|
|
|
dialog.set_default_size(200, 150)
|
2014-09-01 21:07:18 +00:00
|
|
|
|
dialog.set_default_response(Gtk.ResponseType.YES)
|
2014-07-06 20:35:24 +00:00
|
|
|
|
response = dialog.run()
|
2014-09-01 21:07:18 +00:00
|
|
|
|
|
2014-07-06 20:35:24 +00:00
|
|
|
|
if response == Gtk.ResponseType.YES:
|
|
|
|
|
title = self.get_title()
|
|
|
|
|
if self.save_document(widget = None) == Gtk.ResponseType.CANCEL:
|
|
|
|
|
dialog.destroy()
|
|
|
|
|
return self.check_change()
|
|
|
|
|
else:
|
|
|
|
|
dialog.destroy()
|
|
|
|
|
return response
|
|
|
|
|
elif response == Gtk.ResponseType.NO:
|
|
|
|
|
dialog.destroy()
|
|
|
|
|
return response
|
2014-09-01 21:07:18 +00:00
|
|
|
|
else:
|
|
|
|
|
dialog.destroy()
|
|
|
|
|
return Gtk.ResponseType.CANCEL
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
|
|
|
|
def new_document(self, widget):
|
|
|
|
|
if self.check_change() == Gtk.ResponseType.CANCEL:
|
|
|
|
|
return
|
|
|
|
|
else:
|
|
|
|
|
self.TextBuffer.set_text('')
|
|
|
|
|
self.TextEditor.undos = []
|
|
|
|
|
self.TextEditor.redos = []
|
|
|
|
|
|
|
|
|
|
self.did_change = False
|
|
|
|
|
self.filename = None
|
2014-08-08 11:25:57 +00:00
|
|
|
|
self.set_headerbar_title("New File" + self.title_end)
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2014-08-30 08:22:01 +00:00
|
|
|
|
def menu_activate_focusmode(self, widget=None):
|
2014-07-06 20:35:24 +00:00
|
|
|
|
self.focusmode_button.emit('activate')
|
|
|
|
|
|
2014-08-30 08:22:01 +00:00
|
|
|
|
def menu_activate_fullscreen(self, widget=None):
|
2014-07-06 20:35:24 +00:00
|
|
|
|
self.fullscreen_button.emit('activate')
|
|
|
|
|
|
2014-08-30 08:22:01 +00:00
|
|
|
|
def menu_toggle_sidebar(self, widget=None):
|
2014-08-08 11:25:57 +00:00
|
|
|
|
self.sidebar.toggle_sidebar()
|
|
|
|
|
|
2014-08-30 08:22:01 +00:00
|
|
|
|
def menu_activate_preview(self, widget=None):
|
2014-07-06 20:35:24 +00:00
|
|
|
|
self.preview_button.emit('activate')
|
|
|
|
|
|
2014-10-02 17:02:59 +00:00
|
|
|
|
def toggle_spellcheck(self, widget=None, data=None):
|
|
|
|
|
if widget:
|
|
|
|
|
if self.spellcheck:
|
|
|
|
|
if widget.get_active():
|
|
|
|
|
self.SpellChecker.enable()
|
|
|
|
|
else:
|
|
|
|
|
self.SpellChecker.disable()
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2014-10-02 17:02:59 +00:00
|
|
|
|
elif widget.get_active():
|
|
|
|
|
self.SpellChecker = SpellChecker(self.TextEditor, self, locale.getdefaultlocale()[0], collapse=False)
|
|
|
|
|
if self.auto_correct:
|
|
|
|
|
self.auto_correct.set_language(self.SpellChecker.language)
|
|
|
|
|
self.SpellChecker.connect_language_change(self.auto_correct.set_language)
|
|
|
|
|
try:
|
|
|
|
|
self.spellcheck = True
|
|
|
|
|
except:
|
|
|
|
|
self.SpellChecker = None
|
|
|
|
|
self.spellcheck = False
|
|
|
|
|
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(_("Please install 'hunspell' or 'aspell' dictionarys for your language from the software center."))
|
|
|
|
|
response = dialog.run()
|
|
|
|
|
return
|
|
|
|
|
else:
|
|
|
|
|
widget = self.spellcheck_button
|
|
|
|
|
if self.spellcheck and self.SpellChecker.enabled():
|
2014-07-06 20:35:24 +00:00
|
|
|
|
self.SpellChecker.disable()
|
2014-10-02 17:02:59 +00:00
|
|
|
|
widget.set_active(False)
|
|
|
|
|
else:
|
|
|
|
|
self.SpellChecker.enable()
|
|
|
|
|
widget.set_active(True)
|
|
|
|
|
|
2014-07-06 20:35:24 +00:00
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
def on_drag_data_received(self, widget, drag_context, x, y,
|
|
|
|
|
data, info, time):
|
|
|
|
|
"""Handle drag and drop events"""
|
|
|
|
|
if info == 1:
|
|
|
|
|
# uri target
|
|
|
|
|
uris = data.get_uris()
|
|
|
|
|
for uri in uris:
|
|
|
|
|
uri = urllib.parse.unquote_plus(uri)
|
|
|
|
|
mime = mimetypes.guess_type(uri)
|
|
|
|
|
|
|
|
|
|
if mime[0] is not None and mime[0].startswith('image'):
|
2014-08-30 08:22:01 +00:00
|
|
|
|
if uri.startswith("file://"):
|
|
|
|
|
uri = uri[7:]
|
2014-07-06 20:35:24 +00:00
|
|
|
|
text = "![Insert image title here](%s)" % uri
|
|
|
|
|
ll = 2
|
|
|
|
|
lr = 23
|
|
|
|
|
else:
|
|
|
|
|
text = "[Insert link title here](%s)" % uri
|
|
|
|
|
ll = 1
|
|
|
|
|
lr = 22
|
2014-09-01 21:07:18 +00:00
|
|
|
|
self.TextBuffer.place_cursor(self.TextBuffer.get_iter_at_mark(
|
|
|
|
|
self.TextBuffer.get_mark('gtk_drag_target')))
|
2014-07-06 20:35:24 +00:00
|
|
|
|
self.TextBuffer.insert_at_cursor(text)
|
|
|
|
|
insert_mark = self.TextBuffer.get_insert()
|
|
|
|
|
selection_bound = self.TextBuffer.get_selection_bound()
|
|
|
|
|
cursor_iter = self.TextBuffer.get_iter_at_mark(insert_mark)
|
|
|
|
|
cursor_iter.backward_chars(len(text) - ll)
|
|
|
|
|
self.TextBuffer.move_mark(insert_mark, cursor_iter)
|
|
|
|
|
cursor_iter.forward_chars(lr)
|
|
|
|
|
self.TextBuffer.move_mark(selection_bound, cursor_iter)
|
|
|
|
|
|
|
|
|
|
elif info == 2:
|
|
|
|
|
# Text target
|
2014-09-01 21:07:18 +00:00
|
|
|
|
self.TextBuffer.place_cursor(self.TextBuffer.get_iter_at_mark(
|
|
|
|
|
self.TextBuffer.get_mark('gtk_drag_target')))
|
2014-07-06 20:35:24 +00:00
|
|
|
|
self.TextBuffer.insert_at_cursor(data.get_text())
|
2014-09-01 21:07:18 +00:00
|
|
|
|
Gtk.drag_finish(drag_context, True, True, time)
|
2014-07-06 20:35:24 +00:00
|
|
|
|
self.present()
|
2014-09-01 21:07:18 +00:00
|
|
|
|
return False
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
|
|
|
|
def toggle_preview(self, widget, data=None):
|
|
|
|
|
if widget.get_active():
|
|
|
|
|
# Insert a tag with ID to scroll to
|
|
|
|
|
# self.TextBuffer.insert_at_cursor('<span id="scroll_mark"></span>')
|
|
|
|
|
# TODO
|
|
|
|
|
# Find a way to find the next header, scroll to the next header.
|
|
|
|
|
|
|
|
|
|
args = ['pandoc',
|
|
|
|
|
'--from=markdown',
|
|
|
|
|
'--smart',
|
|
|
|
|
'-thtml',
|
|
|
|
|
'--mathjax',
|
|
|
|
|
'-c', helpers.get_media_file('uberwriter.css')]
|
|
|
|
|
|
|
|
|
|
p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
|
|
|
|
|
|
|
|
|
text = bytes(self.get_text(), "utf-8")
|
|
|
|
|
output = p.communicate(text)[0]
|
|
|
|
|
|
|
|
|
|
# Load in Webview and scroll to #ID
|
|
|
|
|
self.webview = WebKit.WebView()
|
|
|
|
|
self.webview.load_html_string(output.decode("utf-8"), 'file://localhost/')
|
|
|
|
|
|
|
|
|
|
# Delete the cursor-scroll mark again
|
|
|
|
|
# cursor_iter = self.TextBuffer.get_iter_at_mark(self.TextBuffer.get_insert())
|
|
|
|
|
# begin_del = cursor_iter.copy()
|
|
|
|
|
# begin_del.backward_chars(30)
|
|
|
|
|
# self.TextBuffer.delete(begin_del, cursor_iter)
|
|
|
|
|
|
2014-08-30 08:22:01 +00:00
|
|
|
|
self.ScrolledWindow.remove(self.EditorViewport)
|
2014-07-06 20:35:24 +00:00
|
|
|
|
self.ScrolledWindow.add(self.webview)
|
|
|
|
|
self.webview.show()
|
|
|
|
|
|
|
|
|
|
# This saying that all links will be opened in default browser, but local files are opened in appropriate apps:
|
|
|
|
|
self.webview.connect("navigation-requested", self.on_click_link)
|
|
|
|
|
else:
|
|
|
|
|
self.ScrolledWindow.remove(self.webview)
|
|
|
|
|
self.webview.destroy()
|
2014-08-30 08:22:01 +00:00
|
|
|
|
self.ScrolledWindow.add(self.EditorViewport)
|
2014-07-06 20:35:24 +00:00
|
|
|
|
self.TextEditor.show()
|
2014-08-30 08:22:01 +00:00
|
|
|
|
|
2014-07-06 20:35:24 +00:00
|
|
|
|
self.queue_draw()
|
2014-09-01 21:07:18 +00:00
|
|
|
|
return True
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
|
|
|
|
def on_click_link(self, view, frame, req, data=None):
|
|
|
|
|
# This provide ability for self.webview to open links in default browser
|
|
|
|
|
if(req.get_uri().startswith("http://")):
|
|
|
|
|
webbrowser.open(req.get_uri())
|
|
|
|
|
return True # Don't let the event "bubble up"
|
|
|
|
|
|
|
|
|
|
def dark_mode_toggled(self, widget, data=None):
|
|
|
|
|
# Save state for saving settings later
|
|
|
|
|
self.dark_mode = widget.get_active()
|
|
|
|
|
if self.dark_mode:
|
|
|
|
|
# Dark Mode is on
|
2014-09-01 21:07:18 +00:00
|
|
|
|
self.gtk_settings.set_property('gtk-application-prefer-dark-theme', True)
|
|
|
|
|
self.get_style_context().add_class("dark_mode")
|
2014-07-06 20:35:24 +00:00
|
|
|
|
self.MarkupBuffer.dark_mode(True)
|
|
|
|
|
else:
|
|
|
|
|
# Dark mode off
|
2014-09-01 21:07:18 +00:00
|
|
|
|
self.gtk_settings.set_property('gtk-application-prefer-dark-theme', False)
|
|
|
|
|
self.get_style_context().remove_class("dark_mode")
|
2014-07-06 20:35:24 +00:00
|
|
|
|
self.MarkupBuffer.dark_mode(False)
|
|
|
|
|
|
|
|
|
|
# Redraw contents of window (self)
|
|
|
|
|
self.queue_draw()
|
|
|
|
|
|
|
|
|
|
def load_file(self, filename=None):
|
|
|
|
|
"""Open File from command line or open / open recent etc."""
|
|
|
|
|
if filename:
|
|
|
|
|
if filename.startswith('file://'):
|
|
|
|
|
filename = filename[7:]
|
|
|
|
|
filename = urllib.parse.unquote_plus(filename)
|
|
|
|
|
try:
|
2014-10-04 22:18:17 +00:00
|
|
|
|
self.preview_button.set_active(False)
|
2014-10-02 17:24:30 +00:00
|
|
|
|
if not os.path.exists(filename):
|
|
|
|
|
self.TextBuffer.set_text("")
|
|
|
|
|
else:
|
|
|
|
|
f = codecs.open(filename, encoding="utf-8", mode='r')
|
|
|
|
|
self.TextBuffer.set_text(f.read())
|
|
|
|
|
f.close()
|
|
|
|
|
self.MarkupBuffer.markup_buffer(0)
|
2014-10-04 22:18:17 +00:00
|
|
|
|
|
|
|
|
|
self.set_headerbar_title(os.path.basename(filename) + self.title_end)
|
|
|
|
|
self.TextEditor.undo_stack = []
|
|
|
|
|
self.TextEditor.redo_stack = []
|
|
|
|
|
self.filename = filename
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.warning("Error Reading File: %r" % e)
|
|
|
|
|
self.did_change = False
|
|
|
|
|
else:
|
|
|
|
|
logger.warning("No File arg")
|
|
|
|
|
|
|
|
|
|
# Help Menu
|
|
|
|
|
def open_launchpad_translation(self, widget, data=None):
|
|
|
|
|
webbrowser.open("https://translations.launchpad.net/uberwriter")
|
|
|
|
|
|
|
|
|
|
def open_launchpad_help(self, widget, data=None):
|
|
|
|
|
webbrowser.open("https://answers.launchpad.net/uberwriter")
|
|
|
|
|
|
|
|
|
|
def open_pandoc_markdown(self, widget, data=None):
|
|
|
|
|
webbrowser.open("http://johnmacfarlane.net/pandoc/README.html#pandocs-markdown")
|
|
|
|
|
|
|
|
|
|
def open_uberwriter_markdown(self, widget, data=None):
|
|
|
|
|
self.load_file(helpers.get_media_file('uberwriter_markdown.md'))
|
|
|
|
|
|
|
|
|
|
def open_search_and_replace(self, widget, data=None):
|
|
|
|
|
self.searchreplace.toggle_search()
|
|
|
|
|
|
|
|
|
|
def open_advanced_export(self, widget, data=None):
|
|
|
|
|
if self.UberwriterAdvancedExportDialog is not None:
|
|
|
|
|
advexp = self.UberwriterAdvancedExportDialog() # pylint: disable=
|
|
|
|
|
|
|
|
|
|
response = advexp.run()
|
|
|
|
|
if response == 1:
|
|
|
|
|
advexp.advanced_export(bytes(self.get_text(), "utf-8"))
|
|
|
|
|
|
|
|
|
|
advexp.destroy()
|
|
|
|
|
|
|
|
|
|
def open_recent(self, widget, data=None):
|
|
|
|
|
if data:
|
|
|
|
|
if self.check_change() == Gtk.ResponseType.CANCEL:
|
|
|
|
|
return
|
|
|
|
|
else:
|
|
|
|
|
self.load_file(data)
|
|
|
|
|
|
|
|
|
|
def generate_recent_files_menu(self, parent_menu):
|
|
|
|
|
# Recent file filter
|
|
|
|
|
self.recent_manager = Gtk.RecentManager.get_default()
|
|
|
|
|
|
|
|
|
|
self.recent_files_menu = Gtk.RecentChooserMenu.new_for_manager(self.recent_manager)
|
|
|
|
|
self.recent_files_menu.set_sort_type(Gtk.RecentSortType.MRU)
|
|
|
|
|
|
|
|
|
|
recent_filter = Gtk.RecentFilter.new()
|
|
|
|
|
recent_filter.add_mime_type('text/x-markdown')
|
|
|
|
|
self.recent_files_menu.set_filter(recent_filter)
|
|
|
|
|
menu = Gtk.Menu.new()
|
|
|
|
|
|
|
|
|
|
for entry in self.recent_files_menu.get_items():
|
|
|
|
|
if entry.exists():
|
|
|
|
|
item = Gtk.MenuItem.new_with_label(entry.get_display_name())
|
|
|
|
|
item.connect('activate', self.open_recent, entry.get_uri())
|
|
|
|
|
menu.append(item)
|
|
|
|
|
item.show()
|
|
|
|
|
|
|
|
|
|
menu.show()
|
|
|
|
|
parent_menu.set_submenu(menu)
|
|
|
|
|
parent_menu.show()
|
|
|
|
|
|
|
|
|
|
def poll_for_motion(self):
|
|
|
|
|
if (self.was_motion == False
|
|
|
|
|
and self.status_bar_visible
|
2014-09-01 21:07:18 +00:00
|
|
|
|
and self.buffer_modified_for_status_bar
|
|
|
|
|
and self.TextEditor.props.has_focus):
|
2014-08-30 08:22:01 +00:00
|
|
|
|
# self.status_bar.set_state_flags(Gtk.StateFlags.INSENSITIVE, True)
|
|
|
|
|
self.statusbar_revealer.set_reveal_child(False)
|
|
|
|
|
self.hb_revealer.set_reveal_child(False)
|
2014-07-06 20:35:24 +00:00
|
|
|
|
self.status_bar_visible = False
|
|
|
|
|
self.buffer_modified_for_status_bar = False
|
|
|
|
|
|
|
|
|
|
self.was_motion = False
|
|
|
|
|
return True
|
|
|
|
|
|
2014-08-30 08:22:01 +00:00
|
|
|
|
def on_motion_notify(self, widget, event, data=None):
|
|
|
|
|
now = event.get_time()
|
|
|
|
|
if now - self.timestamp_last_mouse_motion > 150:
|
2015-02-12 18:09:46 +00:00
|
|
|
|
# filter out accidental motions
|
2014-08-30 08:22:01 +00:00
|
|
|
|
self.timestamp_last_mouse_motion = now
|
|
|
|
|
return
|
|
|
|
|
if now - self.timestamp_last_mouse_motion < 100:
|
2015-02-12 18:09:46 +00:00
|
|
|
|
# filter out accidental motion
|
2014-08-30 08:22:01 +00:00
|
|
|
|
return
|
|
|
|
|
if now - self.timestamp_last_mouse_motion > 100:
|
2015-02-12 18:09:46 +00:00
|
|
|
|
# react on motion by fading in headerbar and statusbar
|
2014-08-30 08:22:01 +00:00
|
|
|
|
if self.status_bar_visible == False:
|
|
|
|
|
self.statusbar_revealer.set_reveal_child(True)
|
|
|
|
|
self.hb_revealer.set_reveal_child(True)
|
|
|
|
|
self.hb.props.opacity = 1
|
|
|
|
|
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-09-01 21:07:18 +00:00
|
|
|
|
def focus_out(self, widget, data=None):
|
|
|
|
|
if self.status_bar_visible == False:
|
|
|
|
|
self.statusbar_revealer.set_reveal_child(True)
|
|
|
|
|
self.hb_revealer.set_reveal_child(True)
|
|
|
|
|
self.hb.props.opacity = 1
|
|
|
|
|
self.status_bar_visible = True
|
|
|
|
|
self.buffer_modified_for_status_bar = False
|
|
|
|
|
self.update_line_and_char_count()
|
|
|
|
|
|
2015-02-12 18:09:46 +00:00
|
|
|
|
|
2014-09-11 15:44:50 +00:00
|
|
|
|
def override_headerbar_background(self, widget, cr):
|
2016-02-05 22:52:43 +00:00
|
|
|
|
# Not needed in Gtk 3.18 anymore apparentlys
|
2014-09-11 15:44:50 +00:00
|
|
|
|
if(widget.get_window().get_state() & self.testbits):
|
|
|
|
|
bg_color = self.get_style_context().get_background_color(Gtk.StateFlags.ACTIVE)
|
|
|
|
|
alloc = widget.get_allocation()
|
|
|
|
|
width = alloc.width
|
|
|
|
|
height = alloc.height
|
|
|
|
|
|
|
|
|
|
cr.rectangle(0,0, width, height)
|
|
|
|
|
cr.set_source_rgb(bg_color.red, bg_color.green, bg_color.blue)
|
|
|
|
|
cr.fill()
|
|
|
|
|
|
|
|
|
|
|
2014-08-30 08:22:01 +00:00
|
|
|
|
def draw_gradient(self, widget, cr):
|
2014-09-01 21:07:18 +00:00
|
|
|
|
bg_color = self.get_style_context().get_background_color(Gtk.StateFlags.ACTIVE)
|
|
|
|
|
|
2014-08-30 08:22:01 +00:00
|
|
|
|
lg_top = cairo.LinearGradient(0, 0, 0, 80)
|
2014-09-01 21:07:18 +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-08-30 08:22:01 +00:00
|
|
|
|
width = widget.get_allocation().width
|
|
|
|
|
height = widget.get_allocation().height
|
2014-09-01 21:07:18 +00:00
|
|
|
|
|
2014-08-30 08:22:01 +00:00
|
|
|
|
cr.rectangle(0, 0, width, 80)
|
|
|
|
|
cr.set_source(lg_top)
|
|
|
|
|
cr.fill()
|
|
|
|
|
cr.rectangle(0, height - 80, width, height)
|
|
|
|
|
|
|
|
|
|
lg_btm = cairo.LinearGradient(0, height - 80, 0, height)
|
2014-09-01 21:07:18 +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-08-30 08:22:01 +00:00
|
|
|
|
|
|
|
|
|
cr.set_source(lg_btm)
|
|
|
|
|
cr.fill()
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2014-10-02 17:02:59 +00:00
|
|
|
|
def use_experimental_features(self, val):
|
2016-02-05 22:52:43 +00:00
|
|
|
|
try:
|
|
|
|
|
self.auto_correct = UberwriterAutoCorrect(self.TextEditor, self.TextBuffer)
|
|
|
|
|
except:
|
|
|
|
|
logger.debug("Couldn't install autocorrect.")
|
2014-10-02 17:02:59 +00:00
|
|
|
|
|
|
|
|
|
|
2014-07-06 20:35:24 +00:00
|
|
|
|
def finish_initializing(self, builder): # pylint: disable=E1002
|
|
|
|
|
"""Set up the main window"""
|
|
|
|
|
super(UberwriterWindow, self).finish_initializing(builder)
|
|
|
|
|
|
|
|
|
|
self.AboutDialog = AboutUberwriterDialog
|
|
|
|
|
self.UberwriterAdvancedExportDialog = UberwriterAdvancedExportDialog
|
|
|
|
|
self.builder = builder
|
|
|
|
|
|
2014-08-30 08:22:01 +00:00
|
|
|
|
self.connect('save-file', self.save_document)
|
|
|
|
|
self.connect('save-file-as', self.save_document_as)
|
|
|
|
|
self.connect('new-file', self.new_document)
|
|
|
|
|
self.connect('open-file', self.open_document)
|
|
|
|
|
self.connect('toggle-fullscreen', self.menu_activate_fullscreen)
|
|
|
|
|
self.connect('toggle-focusmode', self.menu_activate_focusmode)
|
|
|
|
|
self.connect('toggle-preview', self.menu_activate_preview)
|
|
|
|
|
self.connect('toggle-spellcheck', self.toggle_spellcheck)
|
|
|
|
|
self.connect('close-window', self.on_mnu_close_activate)
|
2014-09-01 21:07:18 +00:00
|
|
|
|
self.connect('toggle-search', self.open_search_and_replace)
|
2014-08-30 08:22:01 +00:00
|
|
|
|
self.scroll_adjusted = False
|
2014-09-11 15:44:50 +00:00
|
|
|
|
|
2014-07-06 20:35:24 +00:00
|
|
|
|
# Code for other initialization actions should be added here.
|
|
|
|
|
|
|
|
|
|
# Texlive checker
|
|
|
|
|
self.texlive_installed = False
|
|
|
|
|
|
|
|
|
|
self.set_name('UberwriterWindow')
|
|
|
|
|
|
2014-08-30 08:22:01 +00:00
|
|
|
|
self.use_headerbar = True
|
2014-08-08 11:25:57 +00:00
|
|
|
|
if self.use_headerbar == True:
|
2016-02-05 22:52:43 +00:00
|
|
|
|
self.hb_revealer = Gtk.Revealer(name='titlebar_revealer')
|
2014-08-08 11:25:57 +00:00
|
|
|
|
self.hb = Gtk.HeaderBar()
|
2014-08-30 08:22:01 +00:00
|
|
|
|
self.hb_revealer.add(self.hb)
|
|
|
|
|
self.hb_revealer.props.transition_duration = 1000
|
2014-09-01 21:07:18 +00:00
|
|
|
|
self.hb_revealer.props.transition_type = Gtk.RevealerTransitionType.CROSSFADE
|
2014-08-08 11:25:57 +00:00
|
|
|
|
self.hb.props.show_close_button = True
|
2014-08-30 08:22:01 +00:00
|
|
|
|
self.hb.get_style_context().add_class("titlebar")
|
|
|
|
|
self.set_titlebar(self.hb_revealer)
|
|
|
|
|
self.hb_revealer.show()
|
|
|
|
|
self.hb_revealer.set_reveal_child(True)
|
2014-08-08 11:25:57 +00:00
|
|
|
|
self.hb.show()
|
|
|
|
|
|
|
|
|
|
bbtn = Gtk.MenuButton()
|
2014-08-30 08:22:01 +00:00
|
|
|
|
btn_settings = Gtk.MenuButton()
|
|
|
|
|
btn_settings.props.image = Gtk.Image.new_from_icon_name('emblem-system-symbolic', Gtk.IconSize.BUTTON)
|
2014-10-02 17:02:59 +00:00
|
|
|
|
self.builder.get_object("menu4").detach()
|
2014-08-30 08:22:01 +00:00
|
|
|
|
btn_settings.set_popup(self.builder.get_object("menu4"))
|
2014-10-02 17:02:59 +00:00
|
|
|
|
|
|
|
|
|
self.builder.get_object("menu1").detach()
|
2014-08-08 11:25:57 +00:00
|
|
|
|
bbtn.set_popup(self.builder.get_object("menu1"))
|
|
|
|
|
self.hb.pack_start(bbtn)
|
2014-08-30 08:22:01 +00:00
|
|
|
|
self.hb.pack_end(btn_settings)
|
2014-08-08 11:25:57 +00:00
|
|
|
|
self.hb.show_all()
|
2016-02-05 22:52:43 +00:00
|
|
|
|
self.testbits = Gdk.WindowState.MAXIMIZED
|
2014-09-11 15:44:50 +00:00
|
|
|
|
self.connect('draw', self.override_headerbar_background)
|
2014-09-01 21:07:18 +00:00
|
|
|
|
|
2014-07-06 20:35:24 +00:00
|
|
|
|
self.title_end = " – UberWriter"
|
2014-08-08 11:25:57 +00:00
|
|
|
|
self.set_headerbar_title("New File" + self.title_end)
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
|
|
|
|
self.focusmode = False
|
|
|
|
|
|
|
|
|
|
self.word_count = builder.get_object('word_count')
|
|
|
|
|
self.char_count = builder.get_object('char_count')
|
|
|
|
|
self.menubar = builder.get_object('menubar1')
|
2014-08-30 08:22:01 +00:00
|
|
|
|
self.menubar.hide()
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
|
|
|
|
# Wire up buttons
|
|
|
|
|
self.fullscreen_button = builder.get_object('fullscreen_toggle')
|
|
|
|
|
self.focusmode_button = builder.get_object('focus_toggle')
|
|
|
|
|
self.preview_button = builder.get_object('preview_toggle')
|
|
|
|
|
|
|
|
|
|
self.fullscreen_button.set_name('fullscreen_toggle')
|
|
|
|
|
self.focusmode_button.set_name('focus_toggle')
|
|
|
|
|
self.preview_button.set_name('preview_toggle')
|
|
|
|
|
|
|
|
|
|
# Setup status bar hide after 3 seconds
|
|
|
|
|
|
|
|
|
|
self.status_bar = builder.get_object('status_bar_box')
|
2014-08-30 08:22:01 +00:00
|
|
|
|
self.statusbar_revealer = builder.get_object('status_bar_revealer')
|
2015-05-19 20:32:55 +00:00
|
|
|
|
self.status_bar.get_style_context().add_class('status_bar_box')
|
2014-07-06 20:35:24 +00:00
|
|
|
|
self.status_bar_visible = True
|
|
|
|
|
self.was_motion = True
|
|
|
|
|
self.buffer_modified_for_status_bar = False
|
|
|
|
|
self.connect("motion-notify-event", self.on_motion_notify)
|
|
|
|
|
GObject.timeout_add(3000, self.poll_for_motion)
|
|
|
|
|
|
|
|
|
|
self.accel_group = Gtk.AccelGroup()
|
|
|
|
|
self.add_accel_group(self.accel_group)
|
|
|
|
|
|
|
|
|
|
# Setup light background
|
|
|
|
|
self.TextEditor = TextEditor()
|
2014-09-01 21:07:18 +00:00
|
|
|
|
self.TextEditor.set_name('UberwriterEditor')
|
2015-05-19 20:32:55 +00:00
|
|
|
|
self.get_style_context().add_class('uberwriter_window')
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2014-08-30 08:22:01 +00:00
|
|
|
|
base_leftmargin = 40
|
|
|
|
|
# self.TextEditor.set_left_margin(base_leftmargin)
|
2014-07-06 20:35:24 +00:00
|
|
|
|
self.TextEditor.set_left_margin(40)
|
2014-08-30 08:22:01 +00:00
|
|
|
|
self.TextEditor.props.width_request = 600
|
|
|
|
|
self.TextEditor.props.halign = Gtk.Align.CENTER
|
|
|
|
|
self.TextEditor.set_vadjustment(builder.get_object('vadjustment1'))
|
2014-07-06 20:35:24 +00:00
|
|
|
|
self.TextEditor.set_wrap_mode(Gtk.WrapMode.WORD)
|
2014-09-01 21:07:18 +00:00
|
|
|
|
self.TextEditor.connect('focus-out-event', self.focus_out)
|
2014-09-11 15:44:50 +00:00
|
|
|
|
self.TextEditor.get_style_context().connect('changed', self.style_changed)
|
|
|
|
|
|
|
|
|
|
# self.TextEditor.install_style_property_parser
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
|
|
|
|
self.TextEditor.show()
|
2014-08-30 08:22:01 +00:00
|
|
|
|
self.TextEditor.grab_focus()
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
|
|
|
|
self.ScrolledWindow = builder.get_object('editor_scrolledwindow')
|
2014-08-30 08:22:01 +00:00
|
|
|
|
self.EditorAlignment = builder.get_object('editor_alignment')
|
|
|
|
|
self.EditorAlignment.add(self.TextEditor)
|
|
|
|
|
self.alignment_padding = 40
|
|
|
|
|
self.EditorViewport = builder.get_object('editor_viewport')
|
|
|
|
|
self.EditorViewport.connect_after("draw", self.draw_gradient)
|
|
|
|
|
|
|
|
|
|
self.smooth_scroll_starttime = 0
|
|
|
|
|
self.smooth_scroll_endtime = 0
|
|
|
|
|
self.smooth_scroll_acttarget = 0
|
|
|
|
|
self.smooth_scroll_data = {
|
|
|
|
|
'target_pos': -1,
|
|
|
|
|
'source_pos': -1,
|
|
|
|
|
'duration': 0
|
|
|
|
|
}
|
|
|
|
|
self.smooth_scroll_tickid = -1
|
2014-08-08 11:25:57 +00:00
|
|
|
|
|
2014-07-06 20:35:24 +00:00
|
|
|
|
self.PreviewPane = builder.get_object('preview_scrolledwindow')
|
|
|
|
|
|
|
|
|
|
self.TextEditor.set_margin_top(38)
|
|
|
|
|
self.TextEditor.set_margin_bottom(16)
|
|
|
|
|
|
|
|
|
|
self.TextEditor.set_pixels_above_lines(4)
|
|
|
|
|
self.TextEditor.set_pixels_below_lines(4)
|
|
|
|
|
self.TextEditor.set_pixels_inside_wrap(8)
|
|
|
|
|
|
|
|
|
|
tab_array = Pango.TabArray.new(1, True)
|
|
|
|
|
tab_array.set_tab(0, Pango.TabAlign.LEFT, 20)
|
|
|
|
|
self.TextEditor.set_tabs(tab_array)
|
|
|
|
|
|
|
|
|
|
self.TextBuffer = self.TextEditor.get_buffer()
|
|
|
|
|
self.TextBuffer.set_text('')
|
|
|
|
|
|
|
|
|
|
# Init Window height for top/bottom padding
|
|
|
|
|
self.window_height = self.get_size()[1]
|
|
|
|
|
|
|
|
|
|
self.text_change_event = self.TextBuffer.connect('changed', self.text_changed)
|
|
|
|
|
|
|
|
|
|
# Init file name with None
|
|
|
|
|
self.filename = None
|
|
|
|
|
|
|
|
|
|
self.generate_recent_files_menu(self.builder.get_object('recent'))
|
|
|
|
|
|
|
|
|
|
self.style_provider = Gtk.CssProvider()
|
2014-08-30 08:22:01 +00:00
|
|
|
|
self.style_provider.load_from_path(helpers.get_media_path('style.css'))
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
|
|
|
|
Gtk.StyleContext.add_provider_for_screen(
|
|
|
|
|
Gdk.Screen.get_default(), self.style_provider,
|
|
|
|
|
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Markup and Shortcuts for the TextBuffer
|
|
|
|
|
self.MarkupBuffer = MarkupBuffer(self, self.TextBuffer, base_leftmargin)
|
|
|
|
|
self.MarkupBuffer.markup_buffer()
|
|
|
|
|
|
|
|
|
|
# Scrolling -> Dark or not?
|
|
|
|
|
self.textchange = False
|
|
|
|
|
self.scroll_count = 0
|
2014-08-30 08:22:01 +00:00
|
|
|
|
self.timestamp_last_mouse_motion = 0
|
2014-09-01 21:07:18 +00:00
|
|
|
|
self.TextBuffer.connect_after('mark-set', self.mark_set)
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2014-08-30 08:22:01 +00:00
|
|
|
|
# Drag and drop
|
|
|
|
|
|
2014-09-01 21:07:18 +00:00
|
|
|
|
# self.TextEditor.drag_dest_unset()
|
|
|
|
|
# self.TextEditor.drag_dest_set(Gtk.DestDefaults.ALL, [], Gdk.DragAction.COPY)
|
2014-08-30 08:22:01 +00:00
|
|
|
|
self.target_list = Gtk.TargetList.new([])
|
|
|
|
|
self.target_list.add_uri_targets(1)
|
|
|
|
|
self.target_list.add_text_targets(2)
|
|
|
|
|
|
|
|
|
|
self.TextEditor.drag_dest_set_target_list(self.target_list)
|
2014-09-01 21:07:18 +00:00
|
|
|
|
self.TextEditor.connect_after('drag-data-received', self.on_drag_data_received)
|
|
|
|
|
def on_drop(widget, *args):
|
|
|
|
|
print("drop")
|
|
|
|
|
self.TextEditor.connect('drag-drop', on_drop)
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.TextBuffer.connect('paste-done', self.paste_done)
|
|
|
|
|
# self.connect('key-press-event', self.alt_mod)
|
|
|
|
|
|
|
|
|
|
# Events for Typewriter mode
|
|
|
|
|
|
|
|
|
|
# Setting up inline preview
|
|
|
|
|
self.InlinePreview = UberwriterInlinePreview(self.TextEditor, self.TextBuffer)
|
|
|
|
|
|
|
|
|
|
# Vertical scrolling
|
2014-08-30 08:22:01 +00:00
|
|
|
|
self.vadjustment = self.ScrolledWindow.get_vadjustment()
|
2014-07-06 20:35:24 +00:00
|
|
|
|
self.vadjustment.connect('value-changed', self.scrolled)
|
|
|
|
|
|
2014-10-02 17:02:59 +00:00
|
|
|
|
|
|
|
|
|
self.spellcheck_button = builder.get_object("disable_spellcheck")
|
2014-07-06 20:35:24 +00:00
|
|
|
|
# Setting up spellcheck
|
2014-10-02 17:02:59 +00:00
|
|
|
|
self.auto_correct = None
|
2014-07-06 20:35:24 +00:00
|
|
|
|
try:
|
|
|
|
|
self.SpellChecker = SpellChecker(self.TextEditor,
|
|
|
|
|
locale.getdefaultlocale()[0], collapse=False)
|
2014-10-02 17:02:59 +00:00
|
|
|
|
if self.auto_correct:
|
|
|
|
|
self.auto_correct.set_language(self.SpellChecker.language)
|
|
|
|
|
self.SpellChecker.connect_language_change(self.auto_correct.set_language)
|
|
|
|
|
|
2014-07-06 20:35:24 +00:00
|
|
|
|
self.spellcheck = True
|
|
|
|
|
except:
|
|
|
|
|
self.SpellChecker = None
|
|
|
|
|
self.spellcheck = False
|
|
|
|
|
builder.get_object("disable_spellcheck").set_active(False)
|
|
|
|
|
|
|
|
|
|
if self.spellcheck:
|
|
|
|
|
self.SpellChecker.append_filter('[#*]+', SpellChecker.FILTER_WORD)
|
|
|
|
|
|
|
|
|
|
self.did_change = False
|
|
|
|
|
|
|
|
|
|
###
|
|
|
|
|
# Sidebar initialization test
|
|
|
|
|
###
|
|
|
|
|
self.paned_window = builder.get_object("main_pained")
|
|
|
|
|
self.sidebar_box = builder.get_object("sidebar_box")
|
|
|
|
|
self.sidebar = UberwriterSidebar(self)
|
2014-08-08 11:25:57 +00:00
|
|
|
|
self.sidebar_box.hide()
|
2014-08-30 08:22:01 +00:00
|
|
|
|
|
2014-07-06 20:35:24 +00:00
|
|
|
|
###
|
|
|
|
|
# Search and replace initialization
|
|
|
|
|
# Same interface as Sidebar ;)
|
|
|
|
|
###
|
|
|
|
|
self.searchreplace = UberwriterSearchAndReplace(self)
|
|
|
|
|
|
2014-10-02 17:02:59 +00:00
|
|
|
|
|
2014-07-06 20:35:24 +00:00
|
|
|
|
# Window resize
|
2014-08-30 08:22:01 +00:00
|
|
|
|
self.window_resize(self)
|
2014-07-06 20:35:24 +00:00
|
|
|
|
self.connect("configure-event", self.window_resize)
|
|
|
|
|
self.connect("delete-event", self.on_delete_called)
|
2014-08-30 08:22:01 +00:00
|
|
|
|
|
2014-09-01 21:07:18 +00:00
|
|
|
|
self.gtk_settings = Gtk.Settings.get_default()
|
2014-07-06 20:35:24 +00:00
|
|
|
|
self.load_settings(builder)
|
|
|
|
|
|
2016-02-05 22:52:43 +00:00
|
|
|
|
self.plugins = [BibTex(self)]
|
|
|
|
|
|
|
|
|
|
# self.connect_after('realize', self.color_window)
|
2014-09-01 21:07:18 +00:00
|
|
|
|
|
2016-02-05 22:52:43 +00:00
|
|
|
|
# def color_window(self, widget, data=None):
|
|
|
|
|
# window_gdk = self.get_window()
|
|
|
|
|
# window_gdk.set_background(Gdk.Color(0, 1, 0))
|
2014-09-01 21:07:18 +00:00
|
|
|
|
|
2014-07-06 20:35:24 +00:00
|
|
|
|
def alt_mod(self, widget, event, data=None):
|
|
|
|
|
# TODO: Click and open when alt is pressed
|
|
|
|
|
if event.state & Gdk.ModifierType.MOD2_MASK:
|
|
|
|
|
logger.info("Alt pressed")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
def on_delete_called(self, widget, data=None):
|
|
|
|
|
"""Called when the TexteditorWindow is closed."""
|
|
|
|
|
logger.info('delete called')
|
|
|
|
|
if self.check_change() == Gtk.ResponseType.CANCEL:
|
|
|
|
|
return True
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def on_mnu_close_activate(self, widget, data=None):
|
|
|
|
|
"""
|
|
|
|
|
Signal handler for closing the UberwriterWindow.
|
|
|
|
|
Overriden from parent Window Class
|
|
|
|
|
"""
|
|
|
|
|
if self.on_delete_called(self): # Really destroy?
|
|
|
|
|
return
|
|
|
|
|
else:
|
|
|
|
|
self.destroy()
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
def on_destroy(self, widget, data=None):
|
|
|
|
|
"""Called when the TexteditorWindow is closed."""
|
|
|
|
|
# Clean up code for saving application state should be added here.
|
|
|
|
|
self.save_settings()
|
|
|
|
|
Gtk.main_quit()
|
|
|
|
|
|
2014-08-08 11:25:57 +00:00
|
|
|
|
def set_headerbar_title(self, title):
|
|
|
|
|
if self.use_headerbar:
|
|
|
|
|
self.hb.props.title = title
|
2014-08-30 08:22:01 +00:00
|
|
|
|
self.set_title(title)
|
2014-07-06 20:35:24 +00:00
|
|
|
|
|
2014-08-08 11:25:57 +00:00
|
|
|
|
def save_settings(self):
|
2014-07-06 20:35:24 +00:00
|
|
|
|
if not os.path.exists(CONFIG_PATH):
|
|
|
|
|
try:
|
|
|
|
|
os.makedirs(CONFIG_PATH)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.debug("Failed to make uberwriter config path in\
|
|
|
|
|
~/.config/uberwriter. Error: %r" % e)
|
|
|
|
|
try:
|
|
|
|
|
settings = dict()
|
|
|
|
|
settings["dark_mode"] = self.dark_mode
|
|
|
|
|
settings["spellcheck"] = self.SpellChecker.enabled
|
|
|
|
|
f = open(CONFIG_PATH + "settings.pickle", "wb+")
|
|
|
|
|
pickle.dump(settings, f)
|
|
|
|
|
f.close()
|
|
|
|
|
logger.debug("Saved settings: %r" % settings)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.debug("Error writing settings file to disk. Error: %r" % e)
|
|
|
|
|
|
|
|
|
|
def load_settings(self, builder):
|
|
|
|
|
dark_mode_button = builder.get_object("dark_mode")
|
|
|
|
|
spellcheck_button = builder.get_object("disable_spellcheck")
|
|
|
|
|
try:
|
|
|
|
|
f = open(CONFIG_PATH + "settings.pickle", "rb")
|
|
|
|
|
settings = pickle.load(f)
|
|
|
|
|
f.close()
|
|
|
|
|
self.dark_mode = settings['dark_mode']
|
|
|
|
|
dark_mode_button.set_active(settings['dark_mode'])
|
|
|
|
|
spellcheck_button.set_active(settings['spellcheck'])
|
|
|
|
|
logger.debug("loaded settings: %r" % settings)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.debug("(First Run?) Error loading settings from home dir. \
|
|
|
|
|
Error: %r", e)
|
2015-05-19 20:32:55 +00:00
|
|
|
|
return True
|