removed build

sidebar
Wolf Vollprecht 2014-07-06 22:39:29 +02:00
parent fd22793e8b
commit 08385d6e91
33 changed files with 0 additions and 5438 deletions

View File

@ -1,35 +0,0 @@
# -*- 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
from locale import gettext as _
locale.textdomain('uberwriter')
import logging
logger = logging.getLogger('uberwriter')
from uberwriter_lib.AboutDialog import AboutDialog
# See uberwriter_lib.AboutDialog.py for more details about how this class works.
class AboutUberwriterDialog(AboutDialog):
__gtype_name__ = "AboutUberwriterDialog"
def finish_initializing(self, builder): # pylint: disable=E1002
"""Set up the about dialog"""
super(AboutUberwriterDialog, self).finish_initializing(builder)
# Code for other initialization actions should be added here.

View File

@ -1,209 +0,0 @@
import re
from gi.repository import Gtk
import logging
logger = logging.getLogger('uberwriter')
logger.critical("RASDAOD JKJD ASJD SJ")
class FixTable():
def __init__(self, TextBuffer):
self.TextBuffer = TextBuffer
@staticmethod
def create_seperator(widths, char):
"""
Generate a line of + and - as sepeartor
Example:
>>> create_separarator([2, 4], '-')
'+----+------+'
"""
line = []
for w in widths:
line.append("+" + char * (w + 2))
line.append("+")
return ''.join(line)
@staticmethod
def create_line(columns, widths):
"""
Crea una fila de la tabla separando los campos con un '|'.
El argumento `columns` es una lista con las celdas que se
quieren imprimir y el argumento `widths` tiene el ancho
de cada columna. Si la columna es mas ancha que el texto
a imprimir se agregan espacios vacíos.
Example
>>> create_line(['nombre', 'apellido'], [7, 10])
'| nombre | apellido |'
"""
line = zip(columns, widths)
result = []
for text, width in line:
spaces = " " * (width - len(text))
text = "| " + text + spaces + " "
result.append(text)
result.append("|")
return ''.join(result)
@staticmethod
def create_table(content):
"""Imprime una tabla en formato restructuredText.
El argumento `content` tiene que ser una lista
de celdas.
Example:
>>> print create_table([['software', 'vesion'], ['python', '3.2'],
['vim', '7.3']])
+----------+--------+
| software | vesion |
+==========+========+
| python | 3.2 |
+----------+--------+
| vim | 7.3 |
+----------+--------+
"""
# obtiene las columnas de toda la tabla.
columns = zip(*content)
# calcula el tamano maximo que debe tener cada columna.
# replace with len()
widths = [max([len(x) for x in i]) for i in columns]
result = []
result.append(FixTable.create_seperator(widths, '-'))
print(content, widths)
result.append(FixTable.create_line(content[0], widths))
result.append(FixTable.create_seperator(widths, '='))
for line in content[1:]:
result.append(FixTable.create_line(line, widths))
result.append(FixTable.create_seperator(widths, '-'))
return '\n'.join(result)
@staticmethod
def are_in_a_table(current_line):
"Line in a table?"
return "|" in current_line or "+" in current_line
@staticmethod
def are_in_a_paragraph(current_line):
"Line in a paragraph?"
return len(current_line.strip()) >= 1
def get_table_bounds(self, are_in_callback):
"""
Gets the row number where the table begins and ends.
are_in_callback argument must be a function
        indicating whether a particular line belongs or not
        to the table to be measured (or create).
        Returns two values as a tuple
"""
top = 0
buf = self.TextBuffer
start_iter = buf.get_start_iter()
end_iter = buf.get_end_iter()
text = self.TextBuffer.get_text(start_iter, end_iter, False).split('\n')
logger.debug(text)
length = len(text)
bottom = length - 1
insert_mark = self.TextBuffer.get_insert()
insert_iter = self.TextBuffer.get_iter_at_mark(insert_mark)
current_row_index = insert_iter.get_line()
for a in range(current_row_index, top, -1):
if not are_in_callback(text[a]):
top = a + 1
break
for b in range(current_row_index, length):
if not are_in_callback(text[b]):
bottom = b - 1
break
return top, bottom
@staticmethod
def remove_spaces(string):
"""Remove unnecessary spaces"""
return re.sub("\s\s+", " ", string)
@staticmethod
def create_separators_removing_spaces(string):
return re.sub("\s\s+", "|", string)
@staticmethod
def extract_cells_as_list(string):
"Extrae el texto de una fila de tabla y lo retorna como una lista."
string = FixTable.remove_spaces(string)
return [item.strip() for item in string.split('|') if item]
@staticmethod
def extract_table(text, top, bottom):
full_table_text = text[top:bottom]
# selecciona solamente las lineas que tienen celdas con texto.
only_text_lines = [x for x in full_table_text if '|' in x]
# extrae las celdas y descarta los separadores innecesarios.
return [FixTable.extract_cells_as_list(x) for x in only_text_lines]
@staticmethod
def extract_words_as_lists(text, top, bottom):
"Genera una lista de palabras para crear una lista."
lines = text[top:bottom + 1]
return [FixTable.create_separators_removing_spaces(line).split('|') for line in lines]
def fix_table(self):
"""
Fix Table, so all columns have the same width (again)
`initial_row` is a int idicationg the current row index
"""
cursor_mark = self.TextBuffer.get_insert()
cursor_iter = self.TextBuffer.get_iter_at_mark(cursor_mark)
cursor_iter.set_line(cursor_iter.get_line())
end_line = cursor_iter.copy()
end_line.forward_to_line_end()
line_text = self.TextBuffer.get_text(cursor_iter, end_line, False)
if FixTable.are_in_a_table(line_text):
# obtiene el indice donde comienza y termina la tabla.
r1, r2 = self.get_table_bounds(FixTable.are_in_a_table)
logger.debug('asdasd ')
# extrae de la tabla solo las celdas de texto
buf = self.TextBuffer
start_iter = buf.get_start_iter()
end_iter = buf.get_end_iter()
text = self.TextBuffer.get_text(start_iter, end_iter, False).split('\n')
table_as_list = FixTable.extract_table(text, r1, r2)
logger.debug(table_as_list)
# genera una nueva tabla tipo restructured text y la dibuja en el buffer.
table_content = FixTable.create_table(table_as_list)
logger.debug(table_content)
# Insert table back into Buffer ...
self.TextBuffer.insert(start_iter, table_content, -1)
else:
logger.debug("Not in a table")
print("Not in table")

View File

@ -1,217 +0,0 @@
# -*- 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 re
from gi.repository import Gtk, Gdk # pylint: disable=E0611
from gi.repository import Pango # pylint: disable=E0611
from locale import gettext as _
locale.textdomain('uberwriter')
from . MarkupBuffer import MarkupBuffer
class FormatShortcuts():
def __init__(self, textbuffer, texteditor):
self.TextBuffer = textbuffer
self.TextEditor = texteditor
def rule(self):
self.TextBuffer.insert_at_cursor("\n\n-------\n")
self.TextEditor.scroll_mark_onscreen(self.TextBuffer.get_insert())
self.regex = MarkupBuffer.regex
def bold(self):
self.apply_format("**")
def italic(self):
self.apply_format("*")
def apply_format(self, wrap = "*"):
if self.TextBuffer.get_has_selection():
## Find current highlighting
(start, end) = self.TextBuffer.get_selection_bounds()
moved = False
if (
start.get_offset() >= len(wrap) and
end.get_offset() <= self.TextBuffer.get_char_count() - len(wrap)
):
moved = True
ext_start = start.copy()
ext_start.backward_chars(len(wrap))
ext_end = end.copy()
ext_end.forward_chars(len(wrap))
text = self.TextBuffer.get_text(ext_start, ext_end, True)
else:
text = self.TextBuffer.get_text(start, end, True)
if moved and text.startswith(wrap) and text.endswith(wrap):
text = text[len(wrap):-len(wrap)]
new_text = text
self.TextBuffer.delete(ext_start, ext_end)
move_back = 0
else:
if moved:
text = text[len(wrap):-len(wrap)]
new_text = text.lstrip().rstrip()
text = text.replace(new_text, wrap + new_text + wrap)
self.TextBuffer.delete(start, end)
move_back = len(wrap)
self.TextBuffer.insert_at_cursor(text)
text_length = len(new_text)
else:
helptext = ""
if wrap == "*":
helptext = _("emphasized text")
elif wrap == "**":
helptext = _("strong text")
self.TextBuffer.insert_at_cursor(wrap + helptext + wrap)
text_length = len(helptext)
move_back = len(wrap)
cursor_mark = self.TextBuffer.get_insert()
cursor_iter = self.TextBuffer.get_iter_at_mark(cursor_mark)
cursor_iter.backward_chars(move_back)
self.TextBuffer.move_mark_by_name('selection_bound', cursor_iter)
cursor_iter.backward_chars(text_length)
self.TextBuffer.move_mark_by_name('insert', cursor_iter)
def unordered_list_item(self):
helptext = _("List item")
text_length = len(helptext)
move_back = 0
if self.TextBuffer.get_has_selection():
(start, end) = self.TextBuffer.get_selection_bounds()
if start.starts_line():
text = self.TextBuffer.get_text(start, end, False)
if text.startswith(("- ", "* ", "+ ")):
delete_end = start.forward_chars(2)
self.TextBuffer.delete(start, delete_end)
else:
self.TextBuffer.insert(start, "- ")
else:
move_back = 0
cursor_mark = self.TextBuffer.get_insert()
cursor_iter = self.TextBuffer.get_iter_at_mark(cursor_mark)
start_ext = cursor_iter.copy()
start_ext.backward_lines(3)
text = self.TextBuffer.get_text(cursor_iter, start_ext, False)
lines = text.splitlines()
for line in reversed(lines):
if len(line) and line.startswith(("- ", "* ", "+ ")):
if cursor_iter.starts_line():
self.TextBuffer.insert_at_cursor(line[:2] + helptext)
else:
self.TextBuffer.insert_at_cursor("\n" + line[:2] + helptext)
break
else:
if len(lines[-1]) == 0 and len(lines[-2]) == 0:
self.TextBuffer.insert_at_cursor("- " + helptext)
elif len(lines[-1]) == 0:
if cursor_iter.starts_line():
self.TextBuffer.insert_at_cursor("- " + helptext)
else:
self.TextBuffer.insert_at_cursor("\n- " + helptext)
else:
self.TextBuffer.insert_at_cursor("\n\n- " + helptext)
break
self.select_edit(move_back, text_length)
def ordered_list_item(self):
pass
def select_edit(self, move_back, text_length):
cursor_mark = self.TextBuffer.get_insert()
cursor_iter = self.TextBuffer.get_iter_at_mark(cursor_mark)
cursor_iter.backward_chars(move_back)
self.TextBuffer.move_mark_by_name('selection_bound', cursor_iter)
cursor_iter.backward_chars(text_length)
self.TextBuffer.move_mark_by_name('insert', cursor_iter)
self.TextEditor.scroll_mark_onscreen(self.TextBuffer.get_insert())
def above(self, linestart = ""):
if not cursor_iter.starts_line():
return ""
else:
cursor_mark = self.TextBuffer.get_insert()
cursor_iter = self.TextBuffer.get_iter_at_mark(cursor_mark)
start_ext = cursor_iter.copy()
start_ext.backward_lines(2)
text = self.TextBuffer.get_text(cursor_iter, start_ext, False)
lines = text.splitlines()
#if line[-1].startswith
def get_lines(self, cursor_iter):
start_ext = cursor_iter.copy()
start_ext.backward_lines(2)
text = self.TextBuffer.get_text(cursor_iter, start_ext, False)
lines = text.splitlines()
abs_line = cursor_iter.get_line()
return reversed(lines)
def heading(self, level = 0):
helptext = _("Heading")
before = ""
if self.TextBuffer.get_has_selection():
(start, end) = self.TextBuffer.get_selection_bounds()
text = self.TextBuffer.get_text(start, end, False)
self.TextBuffer.delete(start, end)
else:
text = helptext
cursor_mark = self.TextBuffer.get_insert()
cursor_iter = self.TextBuffer.get_iter_at_mark(cursor_mark)
#lines = self.get_lines(cursor_iter)
#if cursor_iter.starts_line():
# if lines[1] != '':
# before = before + "\n"
#else:
# match = re.match(r'([\#]+ )(.+)', lines[0])
# if match:
# if match.group(1):
#
# print match.group(0)
# if len(match.group(0)) < 6:
# before = before + "#" * (len(match.group(0)) + 1)
# else:
# before = before + "#"
# else:
# before = before + "\n\n"
#
#
# check_text = self.TextBuffer.get_text(start, cursor_iter, False).decode("utf-8")
# print check_text
self.TextBuffer.insert_at_cursor("#" + " " + text)
self.select_edit(0, len(text))

View File

@ -1,316 +0,0 @@
# -*- 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 re
from gi.repository import Gtk # pylint: disable=E0611
from gi.repository import Pango # pylint: disable=E0611
class MarkupBuffer():
def __init__(self, Parent, TextBuffer, base_leftmargin):
self.multiplier = 10
self.parent = Parent
self.TextBuffer = TextBuffer
# Styles
self.italic = self.TextBuffer.create_tag("italic",
style=Pango.Style.ITALIC)
self.emph = self.TextBuffer.create_tag("emph",
weight=Pango.Weight.BOLD,
style=Pango.Style.NORMAL)
self.bolditalic = self.TextBuffer.create_tag("bolditalic",
weight=Pango.Weight.BOLD,
style=Pango.Style.ITALIC)
self.headline_two = self.TextBuffer.create_tag("headline_two",
weight=Pango.Weight.BOLD,
style=Pango.Style.NORMAL)
self.normal_indent = self.TextBuffer.create_tag('normal_indent', indent=100)
self.green_text = self.TextBuffer.create_tag(
"greentext",
foreground="#00364C"
)
self.grayfont = self.TextBuffer.create_tag('graytag',
foreground="gray")
self.blackfont = self.TextBuffer.create_tag('blacktag',
foreground="#222")
self.underline = self.TextBuffer.create_tag(
"underline",
underline=Pango.Underline.SINGLE
)
self.underline.set_property('weight', Pango.Weight.BOLD)
self.strikethrough = self.TextBuffer.create_tag(
"strikethrough",
strikethrough=True
)
self.centertext = self.TextBuffer.create_tag(
"centertext",
justification=Gtk.Justification.CENTER
)
self.TextBuffer.apply_tag(
self.normal_indent,
self.TextBuffer.get_start_iter(),
self.TextBuffer.get_end_iter()
)
self.rev_leftmargin = []
for i in range(0, 6):
name = "rev_marg_indent_left" + str(i)
self.rev_leftmargin.append(self.TextBuffer.create_tag(name))
self.rev_leftmargin[i].set_property("left-margin", 90 - 10 * (i + 1))
self.rev_leftmargin[i].set_property("indent", - 10 * (i + 1) - 10)
#self.leftmargin[i].set_property("background", "gray")
self.leftmargin = []
for i in range(0, 6):
name = "marg_indent_left" + str(i)
self.leftmargin.append(self.TextBuffer.create_tag(name))
self.leftmargin[i].set_property("left-margin", base_leftmargin + 10 + 10 * (i + 1))
self.leftmargin[i].set_property("indent", - 10 * (i + 1) - 10)
self.leftindent = []
for i in range(0, 15):
name = "indent_left" + str(i)
self.leftindent.append(self.TextBuffer.create_tag(name))
self.leftindent[i].set_property("indent", - 10 * (i + 1) - 20)
self.table_env = self.TextBuffer.create_tag('table_env')
self.table_env.set_property('wrap-mode', Gtk.WrapMode.NONE)
# self.table_env.set_property('font', 'Ubuntu Mono 13px')
self.table_env.set_property('pixels-above-lines', 0)
self.table_env.set_property('pixels-below-lines', 0)
regex = {
"ITALIC": re.compile(r"\*\w(.+?)\*| _\w(.+?)_ ", re.UNICODE), # *asdasd* // _asdasd asd asd_
"STRONG": re.compile(r"\*{2}\w(.+?)\*{2}| [_]{2}\w(.+?)[_]{2} ", re.UNICODE), # **as das** // __asdasdasd asd ad a__
"STRONGITALIC": re.compile(r"\*{3}\w(.+?)\*{3}| [_]{3}\w(.+?)[_]{3} "),
"BLOCKQUOTE": re.compile(r"^([\>]+ )", re.MULTILINE),
"STRIKETHROUGH": re.compile(r"~~[^ `~\n].+?~~"),
"LIST": re.compile(r"^[\-\*\+] ", re.MULTILINE),
"NUMERICLIST": re.compile(r"^((\d|[a-z]|\#)+[\.\)]) ", re.MULTILINE),
"INDENTEDLIST": re.compile(r"^(\t{1,6})((\d|[a-z]|\#)+[\.\)]|[\-\*\+]) ", re.MULTILINE),
"HEADINDICATOR": re.compile(r"^(#{1,6}) ", re.MULTILINE),
"HEADLINE": re.compile(r"^(#{1,6} [^\n]+)", re.MULTILINE),
"HEADLINE_TWO": re.compile(r"^\w.+\n[\=\-]{3,}", re.MULTILINE),
"MATH": re.compile(r"[\$]{1,2}([^` ].+?[^`\\ ])[\$]{1,2}"),
"HORIZONTALRULE": re.compile(r"(\n\n[\*\-]{3,}\n)"),
"TABLE": re.compile(r"^[\-\+]{5,}\n(.+?)\n[\-\+]{5,}\n", re.DOTALL),
"LINK": re.compile(r"\(http(.+?)\)")
}
def markup_buffer(self, mode=0):
buf = self.TextBuffer
# Modes:
# 0 -> start to end
# 1 -> around the cursor
# 2 -> n.d.
if mode == 0:
context_start = buf.get_start_iter()
context_end = buf.get_end_iter()
context_offset = 0
elif mode == 1:
cursor_mark = buf.get_insert()
context_start = buf.get_iter_at_mark(cursor_mark)
context_start.backward_lines(3)
context_end = buf.get_iter_at_mark(cursor_mark)
context_end.forward_lines(2)
context_offset = context_start.get_offset()
text = buf.get_slice(context_start, context_end, False)
self.TextBuffer.remove_tag(self.italic, context_start, context_end)
matches = re.finditer(self.regex["ITALIC"], text)
for match in matches:
startIter = buf.get_iter_at_offset(context_offset + match.start())
endIter = buf.get_iter_at_offset(context_offset + match.end())
self.TextBuffer.apply_tag(self.italic, startIter, endIter)
self.TextBuffer.remove_tag(self.emph, context_start, context_end)
matches = re.finditer(self.regex["STRONG"], text)
for match in matches:
startIter = buf.get_iter_at_offset(context_offset + match.start())
endIter = buf.get_iter_at_offset(context_offset + match.end())
self.TextBuffer.apply_tag(self.emph, startIter, endIter)
matches = re.finditer(self.regex["STRONGITALIC"], text)
for match in matches:
startIter = buf.get_iter_at_offset(context_offset + match.start())
endIter = buf.get_iter_at_offset(context_offset + match.end())
self.TextBuffer.apply_tag(self.bolditalic, startIter, endIter)
self.TextBuffer.remove_tag(self.strikethrough, context_start, context_end)
matches = re.finditer(self.regex["STRIKETHROUGH"], text)
for match in matches:
startIter = buf.get_iter_at_offset(context_offset + match.start())
endIter = buf.get_iter_at_offset(context_offset + match.end())
self.TextBuffer.apply_tag(self.strikethrough, startIter, endIter)
self.TextBuffer.remove_tag(self.green_text, context_start, context_end)
matches = re.finditer(self.regex["MATH"], text)
for match in matches:
startIter = buf.get_iter_at_offset(context_offset + match.start())
endIter = buf.get_iter_at_offset(context_offset + match.end())
self.TextBuffer.apply_tag(self.green_text, startIter, endIter)
for margin in self.rev_leftmargin:
self.TextBuffer.remove_tag(margin, context_start, context_end)
matches = re.finditer(self.regex["LIST"], text)
for match in matches:
startIter = buf.get_iter_at_offset(context_offset + match.start())
endIter = buf.get_iter_at_offset(context_offset + match.end())
self.TextBuffer.apply_tag(self.rev_leftmargin[0], startIter, endIter)
matches = re.finditer(self.regex["NUMERICLIST"], text)
for match in matches:
startIter = buf.get_iter_at_offset(context_offset + match.start())
endIter = buf.get_iter_at_offset(context_offset + match.end())
index = len(match.group(1)) - 1
if index < len(self.rev_leftmargin):
margin = self.rev_leftmargin[index]
self.TextBuffer.apply_tag(margin, startIter, endIter)
matches = re.finditer(self.regex["BLOCKQUOTE"], text)
for match in matches:
startIter = buf.get_iter_at_offset(context_offset + match.start())
endIter = buf.get_iter_at_offset(context_offset + match.end())
index = len(match.group(1)) - 2
if index < len(self.leftmargin):
self.TextBuffer.apply_tag(self.leftmargin[index], startIter, endIter)
for leftindent in self.leftindent:
self.TextBuffer.remove_tag(leftindent, context_start, context_end)
matches = re.finditer(self.regex["INDENTEDLIST"], text)
for match in matches:
startIter = buf.get_iter_at_offset(context_offset + match.start())
endIter = buf.get_iter_at_offset(context_offset + match.end())
index = (len(match.group(1)) - 1) * 2 + len(match.group(2))
if index < len(self.leftindent):
self.TextBuffer.apply_tag(self.leftindent[index], startIter, endIter)
matches = re.finditer(self.regex["HEADINDICATOR"], text)
for match in matches:
startIter = buf.get_iter_at_offset(context_offset + match.start())
endIter = buf.get_iter_at_offset(context_offset + match.end())
index = len(match.group(1)) - 1
if index < len(self.rev_leftmargin):
margin = self.rev_leftmargin[index]
self.TextBuffer.apply_tag(margin, startIter, endIter)
matches = re.finditer(self.regex["HORIZONTALRULE"], text)
rulecontext = context_start.copy()
rulecontext.forward_lines(3)
self.TextBuffer.remove_tag(self.centertext, rulecontext, context_end)
for match in matches:
startIter = buf.get_iter_at_offset(context_offset + match.start())
startIter.forward_chars(2)
endIter = buf.get_iter_at_offset(context_offset + match.end())
self.TextBuffer.apply_tag(self.centertext, startIter, endIter)
matches = re.finditer(self.regex["HEADLINE"], text)
for match in matches:
startIter = buf.get_iter_at_offset(context_offset + match.start())
endIter = buf.get_iter_at_offset(context_offset + match.end())
self.TextBuffer.apply_tag(self.emph, startIter, endIter)
matches = re.finditer(self.regex["HEADLINE_TWO"], text)
self.TextBuffer.remove_tag(self.headline_two, rulecontext, context_end)
for match in matches:
startIter = buf.get_iter_at_offset(context_offset + match.start())
endIter = buf.get_iter_at_offset(context_offset + match.end())
self.TextBuffer.apply_tag(self.headline_two, startIter, endIter)
matches = re.finditer(self.regex["TABLE"], text)
for match in matches:
startIter = buf.get_iter_at_offset(context_offset + match.start())
endIter = buf.get_iter_at_offset(context_offset + match.end())
self.TextBuffer.apply_tag(self.table_env, startIter, endIter)
if self.parent.focusmode:
self.focusmode_highlight()
def focusmode_highlight(self):
self.TextBuffer.apply_tag(self.grayfont,
self.TextBuffer.get_start_iter(),
self.TextBuffer.get_end_iter())
self.TextBuffer.remove_tag(self.blackfont,
self.TextBuffer.get_start_iter(),
self.TextBuffer.get_end_iter())
cursor = self.TextBuffer.get_mark("insert")
cursor_iter = self.TextBuffer.get_iter_at_mark(cursor)
end_sentence = cursor_iter.copy()
end_sentence.forward_sentence_end()
end_line = cursor_iter.copy()
end_line.forward_to_line_end()
comp = end_line.compare(end_sentence)
# if comp < 0, end_line is BEFORE end_sentence
if comp <= 0:
end_sentence = end_line
start_sentence = cursor_iter.copy()
start_sentence.backward_sentence_start()
self.TextBuffer.apply_tag(self.blackfont,
start_sentence, end_sentence)
def set_multiplier(self, multiplier):
self.multiplier = multiplier
def recalculate(self, lm):
multiplier = self.multiplier
for i in range(0, 6):
self.rev_leftmargin[i].set_property("left-margin", (lm - multiplier) - multiplier * (i + 1))
self.rev_leftmargin[i].set_property("indent", - multiplier * (i + 1) - multiplier)
for i in range(0, 6):
self.leftmargin[i].set_property("left-margin", (lm - multiplier) + multiplier + multiplier * (i + 1))
self.leftmargin[i].set_property("indent", - (multiplier - 1) * (i + 1) - multiplier)
def dark_mode(self, active=False):
if active:
self.green_text.set_property("foreground", "#FA5B0F")
self.grayfont.set_property("foreground", "#666")
self.blackfont.set_property("foreground", "#CCC")
else:
self.green_text.set_property("foreground", "#00364C")
self.grayfont.set_property("foreground", "gray")
self.blackfont.set_property("foreground", "#222")

View File

@ -1,255 +0,0 @@
# -*- 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
from gi.repository import Gtk
import os
import subprocess
import locale
from locale import gettext as _
locale.textdomain('uberwriter')
import logging
logger = logging.getLogger('uberwriter')
from uberwriter_lib.AdvancedExportDialog import AdvancedExportDialog
# See uberwriter_lib.AboutDialog.py for more details about how this class works.
class UberwriterAdvancedExportDialog(AdvancedExportDialog):
__gtype_name__ = "UberwriterAdvancedExportDialog"
def finish_initializing(self, builder): # pylint: disable=E1002
"""Set up the about dialog"""
super(UberwriterAdvancedExportDialog, self).finish_initializing(builder)
# Code for other initialization actions should be added here.
self.builder.get_object("highlight_style").set_active(0)
format_store = Gtk.ListStore(int, str)
for fmt_id in self.formats_dict:
format_store.append([fmt_id, self.formats_dict[fmt_id]["name"]])
self.format_field = builder.get_object('choose_format')
self.format_field.set_model(format_store)
format_renderer = Gtk.CellRendererText()
self.format_field.pack_start(format_renderer, True)
self.format_field.add_attribute(format_renderer, "text", 1)
self.format_field.set_active(0)
self.show_all()
formats_dict = {
1: {
"name": "LaTeX Source",
"ext": "tex",
"to": "latex"
},
2: {
"name": "LaTeX PDF",
"ext": "pdf",
"to": "pdf"
},
3: {
"name": "LaTeX beamer slide show Source .tex",
"ext": "tex",
"to": "beamer"
},
4: {
"name": "LaTeX beamer slide show PDF",
"ext": "pdf",
"to": "beamer"
},
5: {
"name": "HTML",
"ext": "html",
"to": "html"
},
6: {
"name": "Textile",
"ext": "txt",
"to": "textile"
},
7: {
"name": "OpenOffice text document",
"ext": "odt",
"to": "odt"
},
8: {
"name": "Word docx",
"ext": "docx",
"to": "docx"
},
9: {
"name": "reStructuredText txt",
"ext": "txt",
"to": "rst"
},
10: {
"name": "ConTeXt tex",
"ext": "tex",
"to": "context"
},
11: {
"name": "groff man",
"ext": "man",
"to": "man"
},
12: {
"name": "MediaWiki markup",
"ext": "txt",
"to": "mediawiki"
},
13: {
"name": "OpenDocument XML",
"ext": "xml",
"to": "opendocument"
},
14: {
"name": "OpenDocument XML",
"ext": "texi",
"to": "texinfo"
},
15: {
"name": "Slidy HTML and javascript slide show",
"ext": "html",
"to": "slidy"
},
16: {
"name": "Slideous HTML and javascript slide show",
"ext": "html",
"to": "slideous"
},
17: {
"name": "HTML5 + javascript slide show",
"ext": "html",
"to": "dzslides"
},
18: {
"name": "S5 HTML and javascript slide show",
"ext": "html",
"to": "s5"
},
19: {
"name": "EPub electronic publication",
"ext": "epub",
"to": "epub"
},
20: {
"name": "RTF Rich Text Format",
"ext": "rtf",
"to": "rtf"
}
}
def advanced_export(self, text = ""):
tree_iter = self.format_field.get_active_iter()
if tree_iter != None:
model = self.format_field.get_model()
row_id, name = model[tree_iter][:2]
fmt = self.formats_dict[row_id]
logger.info(fmt)
filechooser = Gtk.FileChooserDialog(
"Export as %s" % fmt["name"],
self,
Gtk.FileChooserAction.SAVE,
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
Gtk.STOCK_SAVE, Gtk.ResponseType.OK)
)
filechooser.set_do_overwrite_confirmation(True)
response = filechooser.run()
if response == Gtk.ResponseType.OK:
filename = filechooser.get_filename()
filechooser.destroy()
else:
filechooser.destroy()
return
output_dir = os.path.abspath(os.path.join(filename, os.path.pardir))
basename = os.path.basename(filename)
args = ['pandoc', '--from=markdown']
to = "--to=%s" % fmt["to"]
if basename.endswith("." + fmt["ext"]):
output_file = "--output=%s" % basename
else:
output_file = "--output=%s.%s" % (basename, fmt["ext"])
if self.builder.get_object("toc").get_active():
args.append('--toc')
if self.builder.get_object("normalize").get_active():
args.append('--normalize')
if self.builder.get_object("smart").get_active():
args.append('--smart')
if self.builder.get_object("highlight").get_active == False:
args.append('--no-highlight')
else:
hs = self.builder.get_object("highlight_style").get_active_text()
args.append("--highlight-style=%s" % hs)
if self.builder.get_object("standalone").get_active():
args.append("--standalone")
if self.builder.get_object("number_sections").get_active():
args.append("--number-sections")
if self.builder.get_object("strict").get_active():
args.append("--strict")
if self.builder.get_object("incremental").get_active():
args.append("--incremental")
if self.builder.get_object("self_contained").get_active():
args.append("--self-contained")
if self.builder.get_object("html5").get_active():
if fmt["to"] == "html":
to = "--to=%s" % "html5"
# Pandoc can't handle verbose -t pdf (#571)
if fmt["to"] != "pdf":
args.append(to)
css_uri = self.builder.get_object("css_filechooser").get_uri()
if css_uri:
if css_uri.startswith("file://"):
css_uri = css_uri[7:]
args.append("--css=%s" % css_uri)
bib_uri = self.builder.get_object("bib_filechooser").get_uri()
if bib_uri:
if bib_uri.startswith("file://"):
bib_uri = bib_uri[7:]
args.append("--bibliography=%s" % bib_uri)
args.append(output_file)
logger.info(args)
p = subprocess.Popen(args, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, cwd=output_dir)
output = p.communicate(text)[0]
return filename

View File

@ -1,288 +0,0 @@
# -*- 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 re
import http.client
import urllib
from urllib.error import URLError, HTTPError
import webbrowser
import locale
import subprocess
import tempfile
import threading
from gi.repository import Gtk, Gdk, GdkPixbuf, GObject
from uberwriter_lib import LatexToPNG
from .FixTable import FixTable
from .MarkupBuffer import MarkupBuffer
from locale import gettext as _
locale.textdomain('uberwriter')
import logging
logger = logging.getLogger('uberwriter')
GObject.threads_init() # Still needed?
# TODO:
# - Don't insert a span with id, it breaks the text to often
# Would be better to search for the nearest title and generate
# A jumping URL from that (for preview)
# Also, after going to preview, set cursor back to where it was
def check_url(url, item, spinner):
logger.debug("thread started, checking url")
error = False
try:
response = urllib.request.urlopen(url)
except URLError as e:
error = True
text = "Error! Reason: %s" % e.reason
if not error:
if (response.code / 100) >= 4:
logger.debug("Website not available")
text = _("Website is not available")
else:
text = _("Website is available")
logger.debug("Response: %s" % text)
spinner.destroy()
item.set_label(text)
def get_web_thumbnail(url, item, spinner):
logger.debug("thread started, generating thumb")
# error = False
filename = tempfile.mktemp(suffix='.png')
thumb_size = '256' # size can only be 32, 64, 96, 128 or 256!
args = ['gnome-web-photo', '--mode=thumbnail', '-s', thumb_size, url, filename]
p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
output = p.communicate()[0]
image = Gtk.Image.new_from_file(filename)
image.show()
# if not error:
# if (response.code / 100) >= 4:
# logger.debug("Website not available")
# text = _("Website is not available")
# else:
# text = _("Website is available")
spinner.destroy()
item.add(image)
item.show()
class UberwriterInlinePreview():
def __init__(self, view, text_buffer):
self.TextView = view
self.TextBuffer = text_buffer
self.LatexConverter = LatexToPNG.LatexToPNG()
cursor_mark = self.TextBuffer.get_insert()
cursor_iter = self.TextBuffer.get_iter_at_mark(cursor_mark)
self.ClickMark = self.TextBuffer.create_mark('click', cursor_iter)
# Events for popup menu
self.TextView.connect_after('populate-popup', self.populate_popup)
self.TextView.connect_after('popup-menu', self.move_popup)
self.TextView.connect('button-press-event', self.click_move_button)
def click_move_button(self, widget, event):
if event.button == 3:
x, y = self.TextView.window_to_buffer_coords(2,
int(event.x),
int(event.y))
self.TextBuffer.move_mark(self.ClickMark,
self.TextView.get_iter_at_location(x, y))
def fix_table(self, widget, data=None):
logger.debug('fixing that table')
f = FixTable(self.TextBuffer)
f.fix_table()
def populate_popup(self, editor, menu, data=None):
item = Gtk.MenuItem.new()
item.set_name("PreviewMenuItem")
separator = Gtk.SeparatorMenuItem.new()
table_item = Gtk.MenuItem.new()
table_item.set_label('Fix that table')
table_item.connect('activate', self.fix_table)
table_item.show()
menu.prepend(table_item)
menu.show()
start_iter = self.TextBuffer.get_iter_at_mark(self.ClickMark)
# Line offset of click mark
line_offset = start_iter.get_line_offset()
end_iter = start_iter.copy()
start_iter.set_line_offset(0)
end_iter.forward_to_line_end()
text = self.TextBuffer.get_text(start_iter, end_iter, False)
math = MarkupBuffer.regex["MATH"]
link = MarkupBuffer.regex["LINK"]
footnote = re.compile('\[\^([^\s]+?)\]')
image = re.compile("!\[(.+?)\]\((.+?)\)")
buf = self.TextBuffer
context_offset = 0
matchlist = []
found_match = False
matches = re.finditer(math, text)
for match in matches:
logger.debug(match.group(1))
if match.start() < line_offset and match.end() > line_offset:
success, result = self.LatexConverter.generatepng(match.group(1))
if success:
image = Gtk.Image.new_from_file(result)
image.show()
item.add(image)
item.set_property('width-request', 50)
else:
label = Gtk.Label()
msg = 'Formula looks incorrect:\n' + result
label.set_alignment(0.0, 0.5)
label.set_text(msg)
label.show()
item.add(label)
item.show()
menu.prepend(separator)
separator.show()
menu.prepend(item)
menu.show()
found_match = True
break
if not found_match:
# Links
matches = re.finditer(link, text)
for match in matches:
if match.start() < line_offset and match.end() > line_offset:
text = text[text.find("http://"):-1]
item.connect("activate", lambda w: webbrowser.open(text))
logger.debug(text)
statusitem = Gtk.MenuItem.new()
statusitem.show()
spinner = Gtk.Spinner.new()
spinner.start()
statusitem.add(spinner)
spinner.show()
thread = threading.Thread(target=check_url,
args=(text, statusitem, spinner))
thread.start()
webphoto_item = Gtk.MenuItem.new()
webphoto_item.show()
spinner_2 = Gtk.Spinner.new()
spinner_2.start()
webphoto_item.add(spinner_2)
spinner_2.show()
thread_image = threading.Thread(target=get_web_thumbnail,
args=(text, webphoto_item, spinner_2))
thread_image.start()
item.set_label(_("Open Link in Webbrowser"))
item.show()
menu.prepend(separator)
separator.show()
menu.prepend(webphoto_item)
menu.prepend(statusitem)
menu.prepend(item)
menu.show()
found_match = True
break
if not found_match:
matches = re.finditer(image, text)
for match in matches:
if match.start() < line_offset and match.end() > line_offset:
path = match.group(2)
if path.startswith("file://"):
path = path[7:]
logger.info(path)
pb = GdkPixbuf.Pixbuf.new_from_file_at_size(path, 400, 300)
image = Gtk.Image.new_from_pixbuf(pb)
image.show()
item.add(image)
item.set_property('width-request', 50)
item.show()
menu.prepend(separator)
separator.show()
menu.prepend(item)
menu.show()
found_match = True
break
if not found_match:
matches = re.finditer(footnote, text)
for match in matches:
if match.start() < line_offset and match.end() > line_offset:
logger.debug(match.group(1))
footnote_match = re.compile("\[\^" + match.group(1) + "\]: (.+(?:\n|\Z)(?:^[\t].+(?:\n|\Z))*)", re.MULTILINE)
replace = re.compile("^\t", re.MULTILINE)
start, end = self.TextBuffer.get_bounds()
fn_match = re.search(footnote_match, self.TextBuffer.get_text(start, end, False))
label = Gtk.Label()
label.set_alignment(0.0, 0.5)
logger.debug(fn_match)
if fn_match:
result = re.sub(replace, "", fn_match.group(1))
if result.endswith("\n"):
result = result[:-1]
else:
result = _("No matching footnote found")
label.set_max_width_chars(40)
label.set_line_wrap(True)
label.set_text(result)
label.show()
item.add(label)
item.show()
menu.prepend(separator)
separator.show()
menu.prepend(item)
menu.show()
found_match = True
break
return
def move_popup(self):
pass

View File

@ -1,150 +0,0 @@
# -*- 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 os, re
import subprocess
from gi.repository import Gtk, Gdk
import time
# from plugins import plugins
import logging
logger = logging.getLogger('uberwriter')
class UberwriterSearchAndReplace():
"""
Adds (regex) search and replace functionality to
uberwriter
"""
def __init__(self, parentwindow):
self.parentwindow = parentwindow
self.box = parentwindow.builder.get_object("searchreplaceholder")
self.searchentry = parentwindow.builder.get_object("searchentrybox")
self.searchentry.connect('changed', self.search)
self.searchentry.connect('activate', self.scrolltonext)
self.open_replace_button = parentwindow.builder.get_object("replace")
self.open_replace_button.connect("toggled", self.toggle_replace)
self.textbuffer = parentwindow.TextBuffer
self.texteditor = parentwindow.TextEditor
self.nextbutton = parentwindow.builder.get_object("next_result")
self.prevbutton = parentwindow.builder.get_object("previous_result")
self.regexbutton = parentwindow.builder.get_object("regex")
self.casesensitivebutton = parentwindow.builder.get_object("case_sensitive")
self.replacebox = parentwindow.builder.get_object("replacebox")
self.replacebox.hide()
self.replace_one_button = parentwindow.builder.get_object("replace_one")
self.replace_all_button = parentwindow.builder.get_object("replace_all")
self.replaceentry = parentwindow.builder.get_object("replaceentrybox")
self.replace_all_button.connect('clicked', self.replace_all)
self.replace_one_button.connect('clicked', self.replace_clicked)
self.replaceentry.connect('activate', self.replace_clicked)
self.nextbutton.connect('clicked', self.scrolltonext)
self.prevbutton.connect('clicked', self.scrolltoprev)
self.regexbutton.connect('toggled', self.search)
self.casesensitivebutton.connect('toggled', self.search)
self.highlight = self.textbuffer.create_tag('search_highlight',
background="yellow")
def toggle_replace(self, widget, data=None):
if widget.get_active():
self.replacebox.show_all()
else:
self.replacebox.hide()
def toggle_search(self, widget=None, data=None):
"""
show search box
"""
if self.box.get_visible():
self.box.hide()
else:
self.box.show()
def search(self, widget=None, data=None, scroll=True):
searchtext = self.searchentry.get_text()
buf = self.textbuffer
context_start = buf.get_start_iter()
context_end = buf.get_end_iter()
text = buf.get_slice(context_start, context_end, False)
self.textbuffer.remove_tag(self.highlight, context_start, context_end)
# case sensitive?
flags = False
if not self.casesensitivebutton.get_active():
flags = flags | re.I
# regex?
if not self.regexbutton.get_active():
searchtext = re.escape(searchtext)
matches = re.finditer(searchtext, text, flags)
self.matchiters = []
self.active = 0
for match in matches:
startIter = buf.get_iter_at_offset(match.start())
endIter = buf.get_iter_at_offset(match.end())
self.matchiters.append((startIter, endIter))
self.textbuffer.apply_tag(self.highlight, startIter, endIter)
if scroll:
self.scrollto(self.active)
logger.debug(searchtext)
def scrolltonext(self, widget, data=None):
self.scrollto(self.active + 1)
def scrolltoprev(self, widget, data=None):
self.scrollto(self.active - 1)
def scrollto(self, index):
if not len(self.matchiters):
return
if(index < len(self.matchiters)):
self.active = index
else:
self.active = 0
matchiter = self.matchiters[self.active]
self.texteditor.scroll_to_iter(matchiter[0], 0.0, True, 0.0, 0.5)
def hide(self):
self.box.hide()
def replace_clicked(self, widget, data=None):
self.replace(self.active)
def replace_all(self, widget=None, data=None):
while self.matchiters:
match = self.matchiters[0]
self.textbuffer.delete(match[0], match[1])
self.textbuffer.insert(match[0], self.replaceentry.get_text())
self.search(scroll=False)
def replace(self, searchindex, inloop=False):
match = self.matchiters[searchindex]
self.textbuffer.delete(match[0], match[1])
self.textbuffer.insert(match[0], self.replaceentry.get_text())
active = self.active
self.search(scroll=False)
self.active = active
self.parentwindow.MarkupBuffer.markup_buffer()
self.scrollto(self.active)

View File

@ -1,176 +0,0 @@
# -*- 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 os
import subprocess
from gi.repository import Gtk, Gdk
# from plugins import plugins
import logging
logger = logging.getLogger('uberwriter')
class Shelve():
"""
Shelve holds a collection of folders
folders:
List of folders
name:
descriptive name of shelve e.g. blog, notes etc.
"""
name = ""
folders = []
def __init__(self, name, folders):
self.name = name
self.folders = folders
def get_tree(self, store):
node = {}
for folder in self.folders:
node[folder] = store.append(None, [os.path.basename(folder), folder])
for root, dirs, files in os.walk(folder):
logger.debug(root)
for directory in dirs:
node[root + "/" + directory] = store.append(node[root], [directory, root + "/" + directory])
for filename in files:
store.append(node[root], [filename, root + "/" + filename])
class UberwriterSidebar():
"""
Presentational class for shelves and files managed by the "sidebar"
parentwindow:
Reference to UberwriterWindow
"""
def __init__(self, parentwindow):
"""
Initialize Treeview and Treestore
"""
self.parentwindow = parentwindow
self.paned_window = parentwindow.paned_window
self.sidebar_box = parentwindow.sidebar_box
# (GtkBox *box,
# GtkWidget *child,
# gboolean expand,
# gboolean fill,
# guint padding);
self.shelve_store = Gtk.ListStore(str)
self.shelve_store.append(["testshelve"])
self.shelves_dropdown = Gtk.ComboBox.new_with_model_and_entry(self.shelve_store)
self.sidebar_box.pack_start(self.shelves_dropdown, False, False, 5)
self.sidebar_scrolledwindow = Gtk.ScrolledWindow()
self.sidebar_scrolledwindow.set_hexpand(True)
self.sidebar_scrolledwindow.set_vexpand(True)
self.store = Gtk.TreeStore(str, str)
self.active_shelf = Shelve("testshelve", ["/home/wolf/Documents/Texte"])
self.active_shelf.get_tree(self.store)
self.treeview = Gtk.TreeView(self.store)
self.treeview.set_headers_visible(False)
# expand first folder (root folder, but not children)
self.treeview.expand_row(Gtk.TreePath.new_from_string("0"), False)
renderer = Gtk.CellRendererText()
column = Gtk.TreeViewColumn("Title", renderer, text=0)
self.treeview.append_column(column)
# new selection
self.treeview.connect('cursor_changed', self.get_selected_file)
# right click handler
self.treeview.connect('button-press-event', self.handle_button_press)
self.treeview.show()
self.sidebar_scrolledwindow.add(self.treeview)
self.sidebar_box.pack_start(self.sidebar_scrolledwindow, True, True, 5)
self.menu_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
self.menu_button = Gtk.MenuButton.new()
# TODO refactor
mb_menu = Gtk.Menu.new()
mitem = Gtk.MenuItem.new_with_label('etstasd asd as d')
mitem.show()
mb_menu.append(mitem)
mb_menu.show()
self.menu_button.set_popup(mb_menu)
self.menu_box.pack_start(self.menu_button, False, False, 5)
self.sidebar_box.pack_end(self.menu_box, False, False, 5)
self.sidebar_box.show_all()
self.paned_window.pack1(self.sidebar_box, True, True);
self.paned_window.show_all()
def get_selected_file(self, widget, data=None):
"""
Handle left click on file
"""
selection = self.treeview.get_selection()
if not selection:
return
selection.set_mode(Gtk.SelectionMode.SINGLE)
treemodel, treeiter = selection.get_selected()
selected_file = treemodel.get_value(treeiter, 1)
self.parentwindow.load_file(selected_file)
logger.debug(selected_file)
def handle_button_press(self, widget, event):
"""
Handle right click (context menu)
"""
if event.type == Gdk.EventType.BUTTON_PRESS and event.button == 3:
# reference to self to not have it garbage collected
self.popup = Gtk.Menu.new()
pathinfo = self.treeview.get_path_at_pos(event.x, event.y)
if pathinfo:
path, col, cellx, celly = pathinfo
treeiter = self.store.get_iter(path)
filename = self.store.get_value(treeiter, 1)
item = Gtk.MenuItem.new()
item.set_label("Open ...")
item.connect("activate", self.context_menu_open_file)
item.filename = filename
item.show()
self.popup.append(item)
self.popup.show()
self.popup.popup(None, None, None, None, event.button, event.time)
return True
def get_treeview(self):
"""
Return Treeview to append to scrolled window
"""
return self.treeview
def context_menu_open_file(self, widget, data=None):
"""
Open selected file with xdg-open
"""
selected_file = widget.filename
subprocess.call(["xdg-open", selected_file])
def close_sidebar(self):
pass

View File

@ -1,440 +0,0 @@
### 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
"""Module for the TextView widgth wich encapsulates management of TextBuffer
and TextIter for common functionality, such as cut, copy, paste, undo, redo,
and highlighting of text.
Using
#create the TextEditor and set the text
editor = TextEditor()
editor.text = "Text to add to the editor"
#use cut, works the same for copy, paste, undo, and redo
def __handle_on_cut(self, widget, data=None):
self.editor.cut()
#add string to highlight
self.editor.add_highlight("Ubuntu")
self.editor.add_highlight("Quickly")
#remove highlights
self.editor.clear_highlight("Ubuntu")
self.editor.clear_all_highlight()
Configuring
#Configure as a TextView
self.editor.set_wrap_mode(Gtk.WRAP_CHAR)
#Access the Gtk.TextBuffer if needed
buffer = self.editor.get_buffer()
Extending
A TextEditor is Gtk.TextView
"""
try:
from gi.repository import Gtk
from gi.repository import Gdk
except:
print("couldn't load depencies")
import logging
logger = logging.getLogger('uberwriter')
class UndoableInsert(object):
"""something that has been inserted into our textbuffer"""
def __init__(self, text_iter, text, length, fflines):
self.offset = text_iter.get_offset() - fflines
self.text = text
self.length = length
if self.length > 1 or self.text in ("\r", "\n", " "):
self.mergeable = False
else:
self.mergeable = True
class UndoableDelete(object):
"""something that has ben deleted from our textbuffer"""
def __init__(self, text_buffer, start_iter, end_iter, fflines):
self.text = text_buffer.get_text(start_iter, end_iter, False)
self.start = start_iter.get_offset() - fflines
self.end = end_iter.get_offset() - fflines
# need to find out if backspace or delete key has been used
# so we don't mess up during redo
insert_iter = text_buffer.get_iter_at_mark(text_buffer.get_insert())
if insert_iter.get_offset() <= self.start:
self.delete_key_used = True
else:
self.delete_key_used = False
if self.end - self.start > 1 or self.text in ("\r", "\n", " "):
self.mergeable = False
else:
self.mergeable = True
class TextEditor(Gtk.TextView):
"""TextEditor encapsulates management of TextBuffer and TextIter for
common functionality, such as cut, copy, paste, undo, redo, and
highlighting of text.
"""
def __init__(self):
"""Create a TextEditor
"""
Gtk.TextView.__init__(self)
self.undo_max = None
self.insert_event = self.get_buffer().connect("insert-text", self.on_insert_text)
self.delete_event = self.get_buffer().connect("delete-range", self.on_delete_range)
display = self.get_display()
self.clipboard = Gtk.Clipboard.get_for_display(display, Gdk.SELECTION_CLIPBOARD)
self.fflines = 0
self.undo_stack = []
self.redo_stack = []
self.not_undoable_action = False
self.undo_in_progress = False
@property
def text(self):
"""
text - a string specifying all the text currently
in the TextEditor's buffer.
This property is read/write.
"""
start_iter = self.get_buffer().get_iter_at_offset(0)
end_iter = self.get_buffer().get_iter_at_offset(-1)
return self.get_buffer().get_text(start_iter, end_iter, False)
@property
def can_undo(self):
return bool(self.undo_stack)
@property
def can_redo(self):
return bool(self.redo_stack)
@text.setter
def text(self, text):
self.get_buffer().set_text(text)
def append(self, text):
"""append: appends text to the end of the textbuffer.
arguments:
text - a string to add to the buffer. The text will be the
last text in the buffer. The insertion cursor will not be moved.
"""
end_iter = self.get_buffer().get_iter_at_offset(-1)
self.get_buffer().insert(end_iter, text)
def prepend(self, text):
"""prepend: appends text to the start of the textbuffer.
arguments:
text - a string to add to the buffer. The text will be the
first text in the buffer. The insertion cursor will not be moved.
"""
start_iter = self.get_buffer().get_iter_at_offset(0)
self.get_buffer().insert(start_iter, text)
insert_iter = self.get_buffer().get_iter_at_offset(len(text)-1)
self.get_buffer().place_cursor(insert_iter)
def cursor_to_end(self):
"""cursor_to_end: moves the insertion curson to the last position
in the buffer.
"""
end_iter = self.get_buffer().get_iter_at_offset(-1)
self.get_buffer().place_cursor(end_iter)
def cursor_to_start(self):
"""cursor_to_start: moves the insertion curson to the first position
in the buffer.
"""
start_iter = self.get_buffer().get_iter_at_offset(0)
self.get_buffer().place_cursor(start_iter)
def cut(self, widget=None, data=None):
"""cut: cut currently selected text and put it on the clipboard.
This function can be called as a function, or assigned as a signal
handler.
"""
self.get_buffer().cut_clipboard(self.clipboard, True)
def copy(self, widget=None, data=None):
"""copy: copy currently selected text to the clipboard.
This function can be called as a function, or assigned as a signal
handler.
"""
self.get_buffer().copy_clipboard(self.clipboard)
def paste(self, widget=None, data=None):
"""paste: Insert any text currently on the clipboard into the
buffer.
This function can be called as a function, or assigned as a signal
handler.
"""
self.get_buffer().paste_clipboard(self.clipboard,None,True)
def undo(self, widget=None, data=None):
"""undo inserts or deletions
undone actions are being moved to redo stack"""
if not self.undo_stack:
return
self.begin_not_undoable_action()
self.undo_in_progress = True
undo_action = self.undo_stack.pop()
self.redo_stack.append(undo_action)
buf = self.get_buffer()
if isinstance(undo_action, UndoableInsert):
offset = undo_action.offset + self.fflines
start = buf.get_iter_at_offset(offset)
stop = buf.get_iter_at_offset(
offset + undo_action.length
)
buf.delete(start, stop)
buf.place_cursor(start)
else:
start = buf.get_iter_at_offset(undo_action.start + self.fflines)
buf.insert(start, undo_action.text)
if undo_action.delete_key_used:
buf.place_cursor(start)
else:
stop = buf.get_iter_at_offset(undo_action.end + self.fflines)
buf.place_cursor(stop)
self.end_not_undoable_action()
self.undo_in_progress = False
def redo(self, widget=None, data=None):
"""redo inserts or deletions
redone actions are moved to undo stack"""
if not self.redo_stack:
return
self.begin_not_undoable_action()
self.undo_in_progress = True
redo_action = self.redo_stack.pop()
self.undo_stack.append(redo_action)
buf = self.get_buffer()
if isinstance(redo_action, UndoableInsert):
start = buf.get_iter_at_offset(redo_action.offset)
buf.insert(start, redo_action.text)
new_cursor_pos = buf.get_iter_at_offset(
redo_action.offset + redo_action.length
)
buf.place_cursor(new_cursor_pos)
else:
start = buf.get_iter_at_offset(redo_action.start)
stop = buf.get_iter_at_offset(redo_action.end)
buf.delete(start, stop)
buf.place_cursor(start)
self.end_not_undoable_action()
self.undo_in_progress = False
def on_insert_text(self, textbuffer, text_iter, text, length):
"""
_on_insert: internal function to handle programatically inserted
text. Do not call directly.
"""
def can_be_merged(prev, cur):
"""see if we can merge multiple inserts here
will try to merge words or whitespace
can't merge if prev and cur are not mergeable in the first place
can't merge when user set the input bar somewhere else
can't merge across word boundaries"""
WHITESPACE = (' ', '\t')
if not cur.mergeable or not prev.mergeable:
return False
elif cur.offset != (prev.offset + prev.length):
return False
elif cur.text in WHITESPACE and not prev.text in WHITESPACE:
return False
elif prev.text in WHITESPACE and not cur.text in WHITESPACE:
return False
return True
if not self.undo_in_progress:
self.redo_stack = []
if self.not_undoable_action:
return
logger.debug(text)
logger.debug("b: %i, l: %i" % (length, len(text)))
undo_action = UndoableInsert(text_iter, text, len(text), self.fflines)
try:
prev_insert = self.undo_stack.pop()
except IndexError:
self.undo_stack.append(undo_action)
return
if not isinstance(prev_insert, UndoableInsert):
self.undo_stack.append(prev_insert)
self.undo_stack.append(undo_action)
return
if can_be_merged(prev_insert, undo_action):
prev_insert.length += undo_action.length
prev_insert.text += undo_action.text
self.undo_stack.append(prev_insert)
else:
self.undo_stack.append(prev_insert)
self.undo_stack.append(undo_action)
def on_delete_range(self, text_buffer, start_iter, end_iter):
"""
On delete
"""
def can_be_merged(prev, cur):
"""see if we can merge multiple deletions here
will try to merge words or whitespace
can't merge if prev and cur are not mergeable in the first place
can't merge if delete and backspace key were both used
can't merge across word boundaries"""
WHITESPACE = (' ', '\t')
if not cur.mergeable or not prev.mergeable:
return False
elif prev.delete_key_used != cur.delete_key_used:
return False
elif prev.start != cur.start and prev.start != cur.end:
return False
elif cur.text not in WHITESPACE and \
prev.text in WHITESPACE:
return False
elif cur.text in WHITESPACE and \
prev.text not in WHITESPACE:
return False
return True
if not self.undo_in_progress:
self.redo_stack = []
if self.not_undoable_action:
return
undo_action = UndoableDelete(text_buffer, start_iter, end_iter, self.fflines)
try:
prev_delete = self.undo_stack.pop()
except IndexError:
self.undo_stack.append(undo_action)
return
if not isinstance(prev_delete, UndoableDelete):
self.undo_stack.append(prev_delete)
self.undo_stack.append(undo_action)
return
if can_be_merged(prev_delete, undo_action):
if prev_delete.start == undo_action.start: # delete key used
prev_delete.text += undo_action.text
prev_delete.end += (undo_action.end - undo_action.start)
else: # Backspace used
prev_delete.text = "%s%s" % (undo_action.text,
prev_delete.text)
prev_delete.start = undo_action.start
self.undo_stack.append(prev_delete)
else:
self.undo_stack.append(prev_delete)
self.undo_stack.append(undo_action)
def begin_not_undoable_action(self):
"""don't record the next actions
toggles self.not_undoable_action"""
self.not_undoable_action = True
def end_not_undoable_action(self):
"""record next actions
toggles self.not_undoable_action"""
self.not_undoable_action = False
class TestWindow(Gtk.Window):
"""For testing and demonstrating AsycnTaskProgressBox.
"""
def __init__(self):
#create a window a VBox to hold the controls
Gtk.Window.__init__(self)
self.set_title("TextEditor Test Window")
windowbox = Gtk.VBox(False, 2)
windowbox.show()
self.add(windowbox)
self.editor = TextEditor()
self.editor.show()
windowbox.pack_end(self.editor, True, True, 0)
self.set_size_request(200,200)
self.show()
self.maximize()
self.connect("destroy", Gtk.main_quit)
self.editor.text = "this is some inserted text"
self.editor.append("\nLine 3")
self.editor.prepend("Line1\n")
self.editor.cursor_to_end()
self.editor.cursor_to_start()
self.editor.undo_max = 100
cut_button = Gtk.Button("Cut")
cut_button.connect("clicked",self.editor.cut)
cut_button.show()
windowbox.pack_start(cut_button, False, False, 0)
copy_button = Gtk.Button("Copy")
copy_button.connect("clicked",self.editor.copy)
copy_button.show()
windowbox.pack_start(copy_button, False, False, 0)
paste_button = Gtk.Button("Paste")
paste_button.connect("clicked",self.editor.paste)
paste_button.show()
windowbox.pack_start(paste_button, False, False, 0)
undo_button = Gtk.Button("Undo")
undo_button.connect("clicked",self.editor.undo)
undo_button.show()
windowbox.pack_start(undo_button, False, False, 0)
redo_button = Gtk.Button("Redo")
redo_button.connect("clicked",self.editor.redo)
redo_button.show()
windowbox.pack_start(redo_button, False, False, 0)
print(self.editor.text)
if __name__== "__main__":
test = TestWindow()
Gtk.main()

File diff suppressed because it is too large Load Diff

View File

@ -1,56 +0,0 @@
# -*- 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 optparse
import locale
import os
from locale import gettext as _
locale.textdomain('uberwriter')
from gi.repository import Gtk # pylint: disable=E0611
from . import UberwriterWindow
from uberwriter_lib import set_up_logging, get_version
def parse_options():
"""Support for command line options"""
parser = optparse.OptionParser(version="%%prog %s" % get_version())
parser.add_option(
"-v", "--verbose", action="count", dest="verbose",
help=_("Show debug messages (-vv debugs uberwriter_lib also)"))
(options, args) = parser.parse_args()
set_up_logging(options)
# print args
return options, args
def main():
'constructor for your class instances'
(options, args) = parse_options()
# Run the application.
if args:
for arg in args:
window = UberwriterWindow.UberwriterWindow()
window.load_file(arg)
else:
window = UberwriterWindow.UberwriterWindow()
window.show()
Gtk.main()

View File

@ -1,50 +0,0 @@
# -*- 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
### DO NOT EDIT THIS FILE ###
from gi.repository import Gtk # pylint: disable=E0611
from . helpers import get_builder
class AboutDialog(Gtk.AboutDialog):
__gtype_name__ = "AboutDialog"
def __new__(cls):
"""Special static method that's automatically called by Python when
constructing a new instance of this class.
Returns a fully instantiated AboutDialog object.
"""
builder = get_builder('AboutUberwriterDialog')
new_object = builder.get_object("about_uberwriter_dialog")
new_object.finish_initializing(builder)
return new_object
def finish_initializing(self, builder):
"""Called while initializing this instance in __new__
finish_initalizing should be called after parsing the ui definition
and creating a AboutDialog object with it in order
to finish initializing the start of the new AboutUberwriterDialog
instance.
Put your initialization code in here and leave __init__ undefined.
"""
# Get a reference to the builder and set up the signals.
self.builder = builder
self.ui = builder.get_ui(self)

View File

@ -1,48 +0,0 @@
# -*- 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
from gi.repository import Gtk # pylint: disable=E0611
from . helpers import get_builder
class AdvancedExportDialog(Gtk.Dialog):
__gtype_name__ = "AdvancedExportDialog"
def __new__(cls):
"""Special static method that's automatically called by Python when
constructing a new instance of this class.
Returns a fully instantiated AdvancedExportDialog object.
"""
builder = get_builder('UberwriterAdvancedExportDialog')
new_object = builder.get_object("uberwriter_advanced_export_dialog")
new_object.finish_initializing(builder)
return new_object
def finish_initializing(self, builder):
"""Called while initializing this instance in __new__
finish_initalizing should be called after parsing the ui definition
and creating a AdvancedExportDialog object with it in order
to finish initializing the start of the new AboutUberwriterDialog
instance.
Put your initialization code in here and leave __init__ undefined.
"""
# Get a reference to the builder and set up the signals.
self.builder = builder
self.ui = builder.get_ui(self)

View File

@ -1,325 +0,0 @@
# -*- 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
### DO NOT EDIT THIS FILE ###
'''Enhances builder connections, provides object to access glade objects'''
from gi.repository import GObject, Gtk # pylint: disable=E0611
import inspect
import functools
import logging
logger = logging.getLogger('uberwriter_lib')
from xml.etree.cElementTree import ElementTree
# this module is big so uses some conventional prefixes and postfixes
# *s list, except self.widgets is a dictionary
# *_dict dictionary
# *name string
# ele_* element in a ElementTree
# pylint: disable=R0904
# the many public methods is a feature of Gtk.Builder
class Builder(Gtk.Builder):
''' extra features
connects glade defined handler to default_handler if necessary
auto connects widget to handler with matching name or alias
auto connects several widgets to a handler via multiple aliases
allow handlers to lookup widget name
logs every connection made, and any on_* not made
'''
def __init__(self):
Gtk.Builder.__init__(self)
self.widgets = {}
self.glade_handler_dict = {}
self.connections = []
self._reverse_widget_dict = {}
# pylint: disable=R0201
# this is a method so that a subclass of Builder can redefine it
def default_handler(self,
handler_name, filename, *args, **kwargs):
'''helps the apprentice guru
glade defined handlers that do not exist come here instead.
An apprentice guru might wonder which signal does what he wants,
now he can define any likely candidates in glade and notice which
ones get triggered when he plays with the project.
this method does not appear in Gtk.Builder'''
logger.debug('''tried to call non-existent function:%s()
expected in %s
args:%s
kwargs:%s''', handler_name, filename, args, kwargs)
# pylint: enable=R0201
def get_name(self, widget):
''' allows a handler to get the name (id) of a widget
this method does not appear in Gtk.Builder'''
return self._reverse_widget_dict.get(widget)
def add_from_file(self, filename):
'''parses xml file and stores wanted details'''
Gtk.Builder.add_from_file(self, filename)
# extract data for the extra interfaces
tree = ElementTree()
tree.parse(filename)
ele_widgets = tree.getiterator("object")
for ele_widget in ele_widgets:
name = ele_widget.attrib['id']
widget = self.get_object(name)
# populate indexes - a dictionary of widgets
self.widgets[name] = widget
# populate a reversed dictionary
self._reverse_widget_dict[widget] = name
# populate connections list
ele_signals = ele_widget.findall("signal")
connections = [
(name,
ele_signal.attrib['name'],
ele_signal.attrib['handler']) for ele_signal in ele_signals]
if connections:
self.connections.extend(connections)
ele_signals = tree.getiterator("signal")
for ele_signal in ele_signals:
self.glade_handler_dict.update(
{ele_signal.attrib["handler"]: None})
def connect_signals(self, callback_obj):
'''connect the handlers defined in glade
reports successful and failed connections
and logs call to missing handlers'''
filename = inspect.getfile(callback_obj.__class__)
callback_handler_dict = dict_from_callback_obj(callback_obj)
connection_dict = {}
connection_dict.update(self.glade_handler_dict)
connection_dict.update(callback_handler_dict)
for item in connection_dict.items():
if item[1] is None:
# the handler is missing so reroute to default_handler
handler = functools.partial(
self.default_handler, item[0], filename)
connection_dict[item[0]] = handler
# replace the run time warning
logger.warn("expected handler '%s' in %s",
item[0], filename)
# connect glade define handlers
Gtk.Builder.connect_signals(self, connection_dict)
# let's tell the user how we applied the glade design
for connection in self.connections:
widget_name, signal_name, handler_name = connection
logger.debug("connect builder by design '%s', '%s', '%s'",
widget_name, signal_name, handler_name)
def get_ui(self, callback_obj=None, by_name=True):
'''Creates the ui object with widgets as attributes
connects signals by 2 methods
this method does not appear in Gtk.Builder'''
result = UiFactory(self.widgets)
# Hook up any signals the user defined in glade
if callback_obj is not None:
# connect glade define handlers
self.connect_signals(callback_obj)
if by_name:
auto_connect_by_name(callback_obj, self)
return result
# pylint: disable=R0903
# this class deliberately does not provide any public interfaces
# apart from the glade widgets
class UiFactory():
''' provides an object with attributes as glade widgets'''
def __init__(self, widget_dict):
self._widget_dict = widget_dict
for (widget_name, widget) in widget_dict.items():
setattr(self, widget_name, widget)
# Mangle any non-usable names (like with spaces or dashes)
# into pythonic ones
cannot_message = """cannot bind ui.%s, name already exists
consider using a pythonic name instead of design name '%s'"""
consider_message = """consider using a pythonic name instead of design name '%s'"""
for (widget_name, widget) in widget_dict.items():
pyname = make_pyname(widget_name)
if pyname != widget_name:
if hasattr(self, pyname):
logger.debug(cannot_message, pyname, widget_name)
else:
logger.debug(consider_message, widget_name)
setattr(self, pyname, widget)
def iterator():
'''Support 'for o in self' '''
return iter(widget_dict.values())
setattr(self, '__iter__', iterator)
def __getitem__(self, name):
'access as dictionary where name might be non-pythonic'
return self._widget_dict[name]
# pylint: enable=R0903
def make_pyname(name):
''' mangles non-pythonic names into pythonic ones'''
pyname = ''
for character in name:
if (character.isalpha() or character == '_' or
(pyname and character.isdigit())):
pyname += character
else:
pyname += '_'
return pyname
# Until bug https://bugzilla.gnome.org/show_bug.cgi?id=652127 is fixed, we
# need to reimplement inspect.getmembers. GObject introspection doesn't
# play nice with it.
def getmembers(obj, check):
members = []
for k in dir(obj):
try:
attr = getattr(obj, k)
except:
continue
if check(attr):
members.append((k, attr))
members.sort()
return members
def dict_from_callback_obj(callback_obj):
'''a dictionary interface to callback_obj'''
methods = getmembers(callback_obj, inspect.ismethod)
aliased_methods = [x[1] for x in methods if hasattr(x[1], 'aliases')]
# a method may have several aliases
#~ @alias('on_btn_foo_clicked')
#~ @alias('on_tool_foo_activate')
#~ on_menu_foo_activate():
#~ pass
alias_groups = [(x.aliases, x) for x in aliased_methods]
aliases = []
for item in alias_groups:
for alias in item[0]:
aliases.append((alias, item[1]))
dict_methods = dict(methods)
dict_aliases = dict(aliases)
results = {}
results.update(dict_methods)
results.update(dict_aliases)
return results
def auto_connect_by_name(callback_obj, builder):
'''finds handlers like on_<widget_name>_<signal> and connects them
i.e. find widget,signal pair in builder and call
widget.connect(signal, on_<widget_name>_<signal>)'''
callback_handler_dict = dict_from_callback_obj(callback_obj)
for item in builder.widgets.items():
(widget_name, widget) = item
signal_ids = []
try:
widget_type = type(widget)
while widget_type:
signal_ids.extend(GObject.signal_list_ids(widget_type))
widget_type = GObject.type_parent(widget_type)
except RuntimeError: # pylint wants a specific error
pass
signal_names = [GObject.signal_name(sid) for sid in signal_ids]
# Now, automatically find any the user didn't specify in glade
for sig in signal_names:
# using convention suggested by glade
sig = sig.replace("-", "_")
handler_names = ["on_%s_%s" % (widget_name, sig)]
# Using the convention that the top level window is not
# specified in the handler name. That is use
# on_destroy() instead of on_windowname_destroy()
if widget is callback_obj:
handler_names.append("on_%s" % sig)
do_connect(item, sig, handler_names,
callback_handler_dict, builder.connections)
log_unconnected_functions(callback_handler_dict, builder.connections)
def do_connect(item, signal_name, handler_names,
callback_handler_dict, connections):
'''connect this signal to an unused handler'''
widget_name, widget = item
for handler_name in handler_names:
target = handler_name in callback_handler_dict.keys()
connection = (widget_name, signal_name, handler_name)
duplicate = connection in connections
if target and not duplicate:
widget.connect(signal_name, callback_handler_dict[handler_name])
connections.append(connection)
logger.debug("connect builder by name '%s','%s', '%s'",
widget_name, signal_name, handler_name)
def log_unconnected_functions(callback_handler_dict, connections):
'''log functions like on_* that we could not connect'''
connected_functions = [x[2] for x in connections]
handler_names = callback_handler_dict.keys()
unconnected = [x for x in handler_names if x.startswith('on_')]
for handler_name in connected_functions:
try:
unconnected.remove(handler_name)
except ValueError:
pass
for handler_name in unconnected:
logger.debug("Not connected to builder '%s'", handler_name)

View File

@ -1,108 +0,0 @@
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
"""
Based on latex2png.py from Stuart Rackham
AUTHOR
Written by Stuart Rackham, <srackham@gmail.com>
The code was inspired by Kjell Magne Fauske's code:
http://fauskes.net/nb/htmleqII/
See also:
http://www.amk.ca/python/code/mt-math
http://code.google.com/p/latexmath2png/
COPYING
Copyright (C) 2010 Stuart Rackham. Free use of this software is
granted under the terms of the MIT License.
"""
import os, sys, tempfile, subprocess
class LatexToPNG():
TEX_HEADER = r'''\documentclass{article}
\usepackage{amsmath}
\usepackage{amsthm}
\usepackage{amssymb}
\usepackage{bm}
\newcommand{\mx}[1]{\mathbf{\bm{#1}}} % Matrix command
\newcommand{\vc}[1]{\mathbf{\bm{#1}}} % Vector command
\newcommand{\T}{\text{T}} % Transpose
\pagestyle{empty}
\begin{document}'''
TEX_FOOTER = r'''\end{document}'''
def __init__(self):
self.temp_result = tempfile.NamedTemporaryFile(suffix='.png')
def latex2png(self, tex, outfile, dpi, modified):
'''Convert LaTeX input file infile to PNG file named outfile.'''
outfile = os.path.abspath(outfile)
outdir = os.path.dirname(outfile)
texfile = tempfile.mktemp(suffix='.tex', dir=os.path.dirname(outfile))
basefile = os.path.splitext(texfile)[0]
dvifile = basefile + '.dvi'
temps = [basefile + ext for ext in ('.tex','.dvi', '.aux', '.log')]
skip = False
tex = '%s\n%s\n%s\n' % (self.TEX_HEADER, tex.strip(), self.TEX_FOOTER)
open(texfile, 'w').write(tex)
saved_pwd = os.getcwd()
os.chdir(outdir)
args = ['latex', '-halt-on-error', texfile]
p = subprocess.Popen(args,
stderr=subprocess.STDOUT,
stdout=subprocess.PIPE)
output = p.stdout
output_lines = output.readlines()
if os.path.isfile(dvifile): # DVI File exists
# Convert DVI file to PNG.
args = ['dvipng',
'-D', str(dpi),
'-T', 'tight',
'-x', '1000',
'-z', '9',
'-bg', 'Transparent',
'-o', outfile,
dvifile]
p = subprocess.Popen(args)
p.communicate()
else:
self.clean_up(temps)
'''
Errors in Latex output start with "! "
Stripping exclamation marks and superflous newlines
and telling the user what he's done wrong.
'''
i = []
error = ""
for line in output_lines:
line = line.decode('utf-8')
if line.startswith("!"):
error += line[2:] # removing "! "
if error.endswith("\n"):
error = error[:-1]
raise Exception(error)
def generatepng(self, formula):
try:
self.temp_result = tempfile.NamedTemporaryFile(suffix='.png')
formula = "$" + formula + "$"
self.latex2png(formula, self.temp_result.name, 300, False)
return (True, self.temp_result.name)
except Exception as e:
self.temp_result.close()
return (False, e.args[0])
def clean_up(self, files):
for f in files:
if os.path.isfile(f):
os.remove(f)

View File

@ -1,64 +0,0 @@
# -*- 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
### DO NOT EDIT THIS FILE ###
"""this dialog adjusts values in gsettings
"""
from gi.repository import Gtk # pylint: disable=E0611
import logging
logger = logging.getLogger('uberwriter_lib')
from . helpers import get_builder, show_uri, get_help_uri
class PreferencesDialog(Gtk.Dialog):
__gtype_name__ = "PreferencesDialog"
def __new__(cls):
"""Special static method that's automatically called by Python when
constructing a new instance of this class.
Returns a fully instantiated PreferencesDialog object.
"""
builder = get_builder('PreferencesUberwriterDialog')
new_object = builder.get_object("preferences_uberwriter_dialog")
new_object.finish_initializing(builder)
return new_object
def finish_initializing(self, builder):
"""Called while initializing this instance in __new__
finish_initalizing should be called after parsing the ui definition
and creating a PreferencesDialog object with it in order to
finish initializing the start of the new PerferencesUberwriterDialog
instance.
Put your initialization code in here and leave __init__ undefined.
"""
# Get a reference to the builder and set up the signals.
self.builder = builder
self.ui = builder.get_ui(self, True)
# code for other initialization actions should be added here
def on_btn_close_clicked(self, widget, data=None):
self.destroy()
def on_btn_help_clicked(self, widget, data=None):
show_uri(self, "ghelp:%s" % get_help_uri('preferences'))

View File

@ -1,129 +0,0 @@
# -*- 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
### DO NOT EDIT THIS FILE ###
from gi.repository import Gio, Gtk # pylint: disable=E0611
import logging
logger = logging.getLogger('uberwriter_lib')
from . helpers import get_builder, show_uri, get_help_uri
# This class is meant to be subclassed by UberwriterWindow. It provides
# common functions and some boilerplate.
class Window(Gtk.Window):
__gtype_name__ = "Window"
# To construct a new instance of this method, the following notable
# methods are called in this order:
# __new__(cls)
# __init__(self)
# finish_initializing(self, builder)
# __init__(self)
#
# For this reason, it's recommended you leave __init__ empty and put
# your initialization code in finish_initializing
def __new__(cls):
"""Special static method that's automatically called by Python when
constructing a new instance of this class.
Returns a fully instantiated BaseUberwriterWindow object.
"""
builder = get_builder('UberwriterWindow')
new_object = builder.get_object("uberwriter_window")
new_object.finish_initializing(builder)
return new_object
def finish_initializing(self, builder):
"""Called while initializing this instance in __new__
finish_initializing should be called after parsing the UI definition
and creating a UberwriterWindow object with it in order to finish
initializing the start of the new UberwriterWindow instance.
"""
# Get a reference to the builder and set up the signals.
self.builder = builder
self.ui = builder.get_ui(self, True)
self.PreferencesDialog = None # class
self.preferences_dialog = None # instance
self.AboutDialog = None # class
self.settings = Gio.Settings("net.launchpad.uberwriter")
self.settings.connect('changed', self.on_preferences_changed)
# Optional application indicator support
# Run 'quickly add indicator' to get started.
# More information:
# http://owaislone.org/quickly-add-indicator/
# https://wiki.ubuntu.com/DesktopExperienceTeam/ApplicationIndicators
try:
from uberwriter import indicator
# self is passed so methods of this class can be called from indicator.py
# Comment this next line out to disable appindicator
self.indicator = indicator.new_application_indicator(self)
except ImportError:
pass
def on_mnu_contents_activate(self, widget, data=None):
show_uri(self, "ghelp:%s" % get_help_uri())
def on_mnu_about_activate(self, widget, data=None):
"""Display the about box for uberwriter."""
if self.AboutDialog is not None:
about = self.AboutDialog() # pylint: disable=E1102
response = about.run()
about.destroy()
def on_mnu_preferences_activate(self, widget, data=None):
"""Display the preferences window for uberwriter."""
""" From the PyGTK Reference manual
Say for example the preferences dialog is currently open,
and the user chooses Preferences from the menu a second time;
use the present() method to move the already-open dialog
where the user can see it."""
if self.preferences_dialog is not None:
logger.debug('show existing preferences_dialog')
self.preferences_dialog.present()
elif self.PreferencesDialog is not None:
logger.debug('create new preferences_dialog')
self.preferences_dialog = self.PreferencesDialog() # pylint: disable=E1102
self.preferences_dialog.connect('destroy', self.on_preferences_dialog_destroyed)
self.preferences_dialog.show()
# destroy command moved into dialog to allow for a help button
def on_mnu_close_activate(self, widget, data=None):
"""Signal handler for closing the UberwriterWindow."""
self.destroy()
def on_destroy(self, widget, data=None):
"""Called when the UberwriterWindow is closed."""
# Clean up code for saving application state should be added here.
Gtk.main_quit()
def on_preferences_changed(self, settings, key, data=None):
logger.debug('preference changed: %s = %s' % (key, str(settings.get_value(key))))
def on_preferences_dialog_destroyed(self, widget, data=None):
'''only affects gui
logically there is no difference between the user closing,
minimising or ignoring the preferences dialog'''
logger.debug('on_preferences_dialog_destroyed')
# to determine whether to create or present preferences_dialog
self.preferences_dialog = None

View File

@ -1,25 +0,0 @@
# -*- 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
### DO NOT EDIT THIS FILE ###
'''facade - makes uberwriter_lib package easy to refactor
while keeping its api constant'''
from . helpers import set_up_logging
from . Window import Window
from . uberwriterconfig import get_version

View File

@ -1,57 +0,0 @@
# -*- coding:utf-8 -*-
#
# Copyright (C) 2012, Maximilian Köhl <linuxmaxi@googlemail.com>
# Copyright (C) 2012, Carlos Jenkins <carlos@jenkins.co.cr>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Python 2/3 unicode
import sys
if sys.version_info.major == 3:
u = lambda x: x
else:
u = lambda x: x.decode('utf-8')
# Metadata
__version__ = '3.0'
__project__ = 'Python GTK Spellcheck'
__short_name__ = 'pygtkspellcheck'
__authors__ = u('Maximilian Köhl & Carlos Jenkins')
__emails__ = u('linuxmaxi@googlemail.com & carlos@jenkins.co.cr')
__website__ = 'http://koehlma.github.com/projects/pygtkspellcheck.html'
__download_url__ = 'https://github.com/koehlma/pygtkspellcheck/tarball/master'
__source__ = 'https://github.com/koehlma/pygtkspellcheck/'
__vcs__ = 'git://github.com/koehlma/pygtkspellcheck.git'
__copyright__ = u('2012, Maximilian Köhl & Carlos Jenkins')
__desc_short__ = 'A simple but quite powerful Python spell checking library for GtkTextViews based on Enchant.'
__desc_long__ = ('It supports both Gtk\'s Python bindings, PyGObject and'
'PyGtk, and for both Python 2 and 3 with automatic switching'
'and binding autodetection. For automatic translation of the'
'user interface it can use GEdit\'s translation files.')
__metadata__ = {'__version__' : __version__,
'__project__' : __project__,
'__short_name__' : __short_name__,
'__authors__' : __authors__,
'__emails__' : __emails__,
'__website__' : __website__,
'__download_url__' : __download_url__,
'__source__' : __source__,
'__vcs__' : __vcs__,
'__copyright__' : __copyright__,
'__desc_short__' : __desc_short__,
'__desc_long__' : __desc_long__}
# import SpellChecker class
from . spellcheck import SpellChecker

View File

@ -1,638 +0,0 @@
# -*- coding:utf-8 -*-
#
# Copyright (C) 2012, Maximilian Köhl <linuxmaxi@googlemail.com>
# Copyright (C) 2012, Carlos Jenkins <carlos@jenkins.co.cr>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
A simple but quite powerful spellchecking library written in pure Python for Gtk
based on Enchant. It supports PyGObject as well as PyGtk for Python 2 and 3 with
automatic switching and binding detection. For automatic translation of the user
interface it can use Gedits translation files.
"""
import enchant
import gettext
import logging
import re
import sys
from ..pylocales import code_to_name
# public objects
__all__ = ['SpellChecker', 'NoDictionariesFound', 'NoGtkBindingFound']
# logger
logger = logging.getLogger(__name__)
class NoDictionariesFound(Exception):
"""
There aren't any dictionaries installed on the current system so
spellchecking could not work in any way.
"""
class NoGtkBindingFound(Exception):
"""
Could not find any loaded Gtk binding.
"""
# find any loaded gtk binding
if 'gi.repository.Gtk' in sys.modules:
gtk = sys.modules['gi.repository.Gtk']
_pygobject = True
elif 'gtk' in sys.modules:
gtk = sys.modules['gtk']
_pygobject = False
else:
raise NoGtkBindingFound('could not find any loaded Gtk binding')
# select base list class
try:
from collections import UserList
_list = UserList
except ImportError:
_list = list
if sys.version_info.major == 3:
_py3k = True
else:
_py3k = False
# select base string
if _py3k:
basestring = str
# map between Gedit's translation and PyGtkSpellcheck's
_GEDIT_MAP = {'Languages' : 'Languages',
'Ignore All' : 'Ignore _All',
'Suggestions' : 'Suggestions',
'(no suggestions)' : '(no suggested words)',
'Add "{}" to Dictionary' : 'Add w_ord'}
# translation
if gettext.find('gedit'):
_gedit = gettext.translation('gedit', fallback=True).gettext
_ = lambda message: _gedit(_GEDIT_MAP[message]).replace('_', '')
else:
locale_name = 'py{}gtkspellcheck'.format(sys.version_info.major)
_ = gettext.translation(locale_name, fallback=True).gettext
class SpellChecker(object):
"""
Main spellchecking class, everything important happens here.
:param view: GtkTextView the SpellChecker should be attached to.
:param language: The language which should be used for spellchecking.
Use a combination of two letter lower-case ISO 639 language code with a
two letter upper-case ISO 3166 country code, for example en_US or de_DE.
:param prefix: A prefix for some internal GtkTextMarks.
:param collapse: Enclose suggestions in its own menu.
:param params: Dictionary with Enchant broker parameters that should be set
e.g. `enchant.myspell.dictionary.path`.
.. attribute:: languages
A list of supported languages.
.. function:: exists(language)
Checks if a language exists.
:param language: language to check
"""
FILTER_WORD = 'word'
FILTER_LINE = 'line'
FILTER_TEXT = 'text'
DEFAULT_FILTERS = {FILTER_WORD : [r'[0-9.,]+'],
FILTER_LINE : [(r'(https?|ftp|file):((//)|(\\\\))+[\w\d:'
r'#@%/;$()~_?+-=\\.&]+'),
r'[\w\d]+@[\w\d.]+'],
FILTER_TEXT : []}
class _LanguageList(_list):
def __init__(self, *args, **kwargs):
if sys.version_info.major == 3:
super().__init__(*args, **kwargs)
else:
_list.__init__(self, *args, **kwargs)
self.mapping = dict(self)
@classmethod
def from_broker(cls, broker):
return cls(sorted([(language, code_to_name(language))
for language in broker.list_languages()],
key=lambda language: language[1]))
def exists(self, language):
return language in self.mapping
class _Mark():
def __init__(self, buffer, name, start):
self._buffer = buffer
self._name = name
self._mark = self._buffer.create_mark(self._name, start, True)
@property
def iter(self):
return self._buffer.get_iter_at_mark(self._mark)
@property
def inside_word(self):
return self.iter.inside_word()
@property
def word(self):
start = self.iter
if not start.starts_word():
start.backward_word_start()
end = self.iter
if end.inside_word():
end.forward_word_end()
return start, end
def move(self, location):
self._buffer.move_mark(self._mark, location)
def __init__(self, view, language='en', prefix='gtkspellchecker',
collapse=True, params={}):
self._view = view
self.collapse = collapse
self._view.connect('populate-popup',
lambda entry, menu:self._extend_menu(menu))
self._view.connect('popup-menu', self._click_move_popup)
self._view.connect('button-press-event', self._click_move_button)
self._prefix = prefix
if _pygobject:
self._misspelled = gtk.TextTag.new('{}-misspelled'\
.format(self._prefix))
else:
self._misspelled = gtk.TextTag('{}-misspelled'.format(self._prefix))
self._misspelled.set_property('underline', 4)
self._broker = enchant.Broker()
for param, value in params.items(): self._broker.set_param(param, value)
self.languages = SpellChecker._LanguageList.from_broker(self._broker)
if self.languages.exists(language):
self._language = language
elif self.languages.exists('en'):
logger.warning(('no installed dictionary for language "{}", '
'fallback to english'.format(language)))
self._language = 'en'
else:
if self.languages:
self._language = self.languages[0][0]
logger.warning(('no installed dictionary for language "{}" '
'and english, fallback to first language in'
'language list ("{}")').format(language,
self._language))
else:
logger.critical('no dictionaries found')
raise NoDictionariesFound()
self._dictionary = self._broker.request_dict(self._language)
self._deferred_check = False
self._filters = dict(SpellChecker.DEFAULT_FILTERS)
self._regexes = {SpellChecker.FILTER_WORD : re.compile('|'.join(
self._filters[SpellChecker.FILTER_WORD])),
SpellChecker.FILTER_LINE : re.compile('|'.join(
self._filters[SpellChecker.FILTER_LINE])),
SpellChecker.FILTER_TEXT : re.compile('|'.join(
self._filters[SpellChecker.FILTER_TEXT]),
re.MULTILINE)}
self._enabled = True
self.buffer_initialize()
@property
def language(self):
"""
The language used for spellchecking.
"""
return self._language
@language.setter
def language(self, language):
if language != self._language and self.languages.exists(language):
self._language = language
self._dictionary = self._broker.request_dict(language)
self.recheck()
@property
def enabled(self):
"""
Enable or disable spellchecking.
"""
return self._enabled
@enabled.setter
def enabled(self, enabled):
if enabled and not self._enabled:
self.enable()
elif not enabled and self._enabled:
self.disable()
def buffer_initialize(self):
"""
Initialize the GtkTextBuffer associated with the GtkTextView. If you
have associated a new GtkTextBuffer with the GtkTextView call this
method.
"""
self._buffer = self._view.get_buffer()
self._buffer.connect('insert-text', self._before_text_insert)
self._buffer.connect_after('insert-text', self._after_text_insert)
self._buffer.connect_after('delete-range', self._range_delete)
self._buffer.connect_after('mark-set', self._mark_set)
start = self._buffer.get_bounds()[0]
self._marks = {'insert-start' : SpellChecker._Mark(self._buffer,
'{}-insert-start'.format(self._prefix), start),
'insert-end' : SpellChecker._Mark(self._buffer,
'{}-insert-end'.format(self._prefix), start),
'click' : SpellChecker._Mark(self._buffer,
'{}-click'.format(self._prefix), start)}
self._table = self._buffer.get_tag_table()
self._table.add(self._misspelled)
self.ignored_tags = []
def tag_added(tag, *args):
if hasattr(tag, 'spell_check') and not getattr(tag, 'spell_check'):
self.ignored_tags.append(tag)
def tag_removed(tag, *args):
if tag in self.ignored_tags:
self.ignored_tags.remove(tag)
self._table.connect('tag-added', tag_added)
self._table.connect('tag-removed', tag_removed)
self._table.foreach(tag_added, None)
self.no_spell_check = self._table.lookup('no-spell-check')
if not self.no_spell_check:
if _pygobject:
self.no_spell_check = gtk.TextTag.new('no-spell-check')
else:
self.no_spell_check = gtk.TextTag('no-spell-check')
self._table.add(self.no_spell_check)
self.recheck()
def recheck(self):
"""
Rechecks the spelling of the whole text.
"""
start, end = self._buffer.get_bounds()
self.check_range(start, end, True)
def disable(self):
"""
Disable spellchecking.
"""
self._enabled = False
start, end = self._buffer.get_bounds()
self._buffer.remove_tag(self._misspelled, start, end)
def enable(self):
"""
Enable spellchecking.
"""
self._enabled = True
self.recheck()
def append_filter(self, regex, filter_type):
"""
Append a new filter to the filter list. Filters are useful to ignore
some misspelled words based on regular expressions.
:param regex: The regex used for filtering.
:param filter_type: The type of the filter.
Filter Types:
:const:`SpellChecker.FILTER_WORD`: The regex must match the whole word
you want to filter. The word separation is done by Pango's word
separation algorithm so, for example, urls won't work here because
they are split in many words.
:const:`SpellChecker.FILTER_LINE`: If the expression you want to match
is a single line expression use this type. It should not be an open
end expression because then the rest of the line with the text you
want to filter will become correct.
:const:`SpellChecker.FILTER_TEXT`: Use this if you want to filter
multiline expressions. The regex will be compiled with the
`re.MULTILINE` flag. Same with open end expressions apply here.
"""
self._filters[filter_type].append(regex)
if filter_type == SpellChecker.FILTER_TEXT:
self._regexes[filter_type] = re.compile('|'.join(
self._filters[filter_type]), re.MULTILINE)
else:
self._regexes[filter_type] = re.compile('|'.join(
self._filters[filter_type]))
def remove_filter(self, regex, filter_type):
"""
Remove a filter from the filter list.
:param regex: The regex which used for filtering.
:param filter_type: The type of the filter.
"""
self._filters[filter_type].remove(regex)
if filter_type == SpellChecker.FILTER_TEXT:
self._regexes[filter_type] = re.compile('|'.join(
self._filters[filter_type]), re.MULTILINE)
else:
self._regexes[filter_type] = re.compile('|'.join(
self._filters[filter_type]))
def append_ignore_tag(self, tag):
"""
Appends a tag to the list of ignored tags. A string will be automatic
resolved into a tag object.
:param tag: Tag object or tag name.
"""
if isinstance(tag, basestring):
tag = self._table.lookup(tag)
self.ignored_tags.append(tag)
def remove_ignore_tag(self, tag):
"""
Removes a tag from the list of ignored tags. A string will be automatic
resolved into a tag object.
:param tag: Tag object or tag name.
"""
if isinstance(tag, basestring):
tag = self._table.lookup(tag)
self.ignored_tags.remove(tag)
def add_to_dictionary(self, word):
"""
Adds a word to user's dictionary.
:param word: The word to add.
"""
self._dictionary.add_to_pwl(word)
self.recheck()
def ignore_all(self, word):
"""
Ignores a word for the current session.
:param word: The word to ignore.
"""
self._dictionary.add_to_session(word)
self.recheck()
def check_range(self, start, end, force_all=False):
"""
Checks a specified range between two GtkTextIters.
:param start: Start iter - checking starts here.
:param end: End iter - checking ends here.
"""
if not self._enabled:
return
if end.inside_word(): end.forward_word_end()
if not start.starts_word() and (start.inside_word() or
start.ends_word()):
start.backward_word_start()
self._buffer.remove_tag(self._misspelled, start, end)
cursor = self._buffer.get_iter_at_mark(self._buffer.get_insert())
precursor = cursor.copy()
precursor.backward_char()
highlight = (cursor.has_tag(self._misspelled) or
precursor.has_tag(self._misspelled))
if not start.get_offset():
start.forward_word_end()
start.backward_word_start()
word_start = start.copy()
while word_start.compare(end) < 0:
word_end = word_start.copy()
word_end.forward_word_end()
in_word = ((word_start.compare(cursor) < 0) and
(cursor.compare(word_end) <= 0))
if in_word and not force_all:
if highlight:
self._check_word(word_start, word_end)
else:
self._deferred_check = True
else:
self._check_word(word_start, word_end)
self._deferred_check = False
word_end.forward_word_end()
word_end.backward_word_start()
if word_start.equal(word_end):
break
word_start = word_end.copy()
def _languages_menu(self):
def _set_language(item, code):
self.language = code
if _pygobject:
menu = gtk.Menu.new()
group = []
else:
menu = gtk.Menu()
group = gtk.RadioMenuItem()
connect = []
for code, name in self.languages:
if _pygobject:
item = gtk.RadioMenuItem.new_with_label(group, name)
group.append(item)
else:
item = gtk.RadioMenuItem(group, name)
if code == self.language:
item.set_active(True)
connect.append((item, code))
menu.append(item)
for item, code in connect:
item.connect('activate', _set_language, code)
return menu
def _suggestion_menu(self, word):
menu = []
suggestions = self._dictionary.suggest(word)
if not suggestions:
if _pygobject:
item = gtk.MenuItem.new()
label = gtk.Label.new('')
else:
item = gtk.MenuItem()
label = gtk.Label()
try:
label.set_halign(gtk.Align.LEFT)
except AttributeError:
label.set_alignment(0.0, 0.5)
label.set_markup('<i>{text}</i>'.format(text=_('(no suggestions)')))
item.add(label)
menu.append(item)
else:
for suggestion in suggestions:
if _pygobject:
item = gtk.MenuItem.new()
label = gtk.Label.new('')
else:
item = gtk.MenuItem()
label = gtk.Label()
label.set_markup('<b>{text}</b>'.format(text=suggestion))
try:
label.set_halign(gtk.Align.LEFT)
except AttributeError:
label.set_alignment(0.0, 0.5)
item.add(label)
item.connect('activate', self._replace_word, word, suggestion)
menu.append(item)
if _pygobject:
menu.append(gtk.SeparatorMenuItem.new())
item = gtk.MenuItem.new_with_label(
_('Add "{}" to Dictionary').format(word))
else:
menu.append(gtk.SeparatorMenuItem())
item = gtk.MenuItem(_('Add "{}" to Dictionary').format(word))
item.connect('activate', lambda *args: self.add_to_dictionary(word))
menu.append(item)
if _pygobject:
item = gtk.MenuItem.new_with_label(_('Ignore All'))
else:
item = gtk.MenuItem(_('Ignore All'))
item.connect('activate', lambda *args: self.ignore_all(word))
menu.append(item)
return menu
def _extend_menu(self, menu):
if not self._enabled:
return
if _pygobject:
separator = gtk.SeparatorMenuItem.new()
else:
separator = gtk.SeparatorMenuItem()
separator.show()
menu.prepend(separator)
if _pygobject:
languages = gtk.MenuItem.new_with_label(_('Languages'))
else:
languages = gtk.MenuItem(_('Languages'))
languages.set_submenu(self._languages_menu())
languages.show_all()
menu.prepend(languages)
if self._marks['click'].inside_word:
start, end = self._marks['click'].word
if start.has_tag(self._misspelled):
if _py3k:
word = self._buffer.get_text(start, end, False)
else:
word = self._buffer.get_text(start, end,
False).decode('utf-8')
items = self._suggestion_menu(word)
if self.collapse:
if _pygobject:
suggestions = gtk.MenuItem.new_with_label(
_('Suggestions'))
submenu = gtk.Menu.new()
else:
suggestions = gtk.MenuItem(_('Suggestions'))
submenu = gtk.Menu()
for item in items:
submenu.append(item)
suggestions.set_submenu(submenu)
suggestions.show_all()
menu.prepend(suggestions)
else:
items.reverse()
for item in items:
menu.prepend(item)
menu.show_all()
def _click_move_popup(self, *args):
self._marks['click'].move(self._buffer.get_iter_at_mark(
self._buffer.get_insert()))
return False
def _click_move_button(self, widget, event):
if event.button == 3:
if self._deferred_check: self._check_deferred_range(True)
x, y = self._view.window_to_buffer_coords(2, int(event.x),
int(event.y))
self._marks['click'].move(self._view.get_iter_at_location(x, y))
return False
def _before_text_insert(self, textbuffer, location, text, length):
self._marks['insert-start'].move(location)
def _after_text_insert(self, textbuffer, location, text, length):
start = self._marks['insert-start'].iter
self.check_range(start, location)
self._marks['insert-end'].move(location)
def _range_delete(self, textbuffer, start, end):
self.check_range(start, end)
def _mark_set(self, textbuffer, location, mark):
if mark == self._buffer.get_insert() and self._deferred_check:
self._check_deferred_range(False)
def _replace_word(self, item, old_word, new_word):
start, end = self._marks['click'].word
offset = start.get_offset()
self._buffer.begin_user_action()
self._buffer.delete(start, end)
self._buffer.insert(self._buffer.get_iter_at_offset(offset), new_word)
self._buffer.end_user_action()
self._dictionary.store_replacement(old_word, new_word)
def _check_deferred_range(self, force_all):
start = self._marks['insert-start'].iter
end = self._marks['insert-end'].iter
self.check_range(start, end, force_all)
def _check_word(self, start, end):
if start.has_tag(self.no_spell_check):
return
for tag in self.ignored_tags:
if start.has_tag(tag):
return
if _py3k:
word = self._buffer.get_text(start, end, False).strip()
else:
word = self._buffer.get_text(start, end, False).decode('utf-8').strip()
if len(self._filters[SpellChecker.FILTER_WORD]):
if self._regexes[SpellChecker.FILTER_WORD].match(word):
return
if len(self._filters[SpellChecker.FILTER_LINE]):
line_start = self._buffer.get_iter_at_line(start.get_line())
line_end = end.copy()
line_end.forward_to_line_end()
if _py3k:
line = self._buffer.get_text(line_start, line_end, False)
else:
line = self._buffer.get_text(line_start, line_end,
False).decode('utf-8')
for match in self._regexes[SpellChecker.FILTER_LINE].finditer(line):
if match.start() <= start.get_line_offset() <= match.end():
start = self._buffer.get_iter_at_line_offset(
start.get_line(), match.start())
end = self._buffer.get_iter_at_line_offset(start.get_line(),
match.end())
self._buffer.remove_tag(self._misspelled, start, end)
return
if len(self._filters[SpellChecker.FILTER_TEXT]):
text_start, text_end = self._buffer.get_bounds()
if _py3k:
text = self._buffer.get_text(text_start, text_end, False)
else:
text = self._buffer.get_text(text_start, text_end,
False).decode('utf-8')
for match in self._regexes[SpellChecker.FILTER_TEXT].finditer(text):
if match.start() <= start.get_offset() <= match.end():
start = self._buffer.get_iter_at_offset(match.start())
end = self._buffer.get_iter_at_offset(match.end())
self._buffer.remove_tag(self._misspelled, start, end)
return
if not self._dictionary.check(word):
self._buffer.apply_tag(self._misspelled, start, end)

View File

@ -1,117 +0,0 @@
# -*- 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
### DO NOT EDIT THIS FILE ###
"""Helpers for an Ubuntu application."""
import logging
import os
from . uberwriterconfig import get_data_file
from . Builder import Builder
from locale import gettext as _
def get_builder(builder_file_name):
"""Return a fully-instantiated Gtk.Builder instance from specified ui
file
:param builder_file_name: The name of the builder file, without extension.
Assumed to be in the 'ui' directory under the data path.
"""
# Look for the ui file that describes the user interface.
ui_filename = get_data_file('ui', '%s.ui' % (builder_file_name,))
if not os.path.exists(ui_filename):
ui_filename = None
builder = Builder()
builder.set_translation_domain('uberwriter')
builder.add_from_file(ui_filename)
return builder
# Owais Lone : To get quick access to icons and stuff.
def get_media_file(media_file_name):
media_filename = get_data_file('media', '%s' % (media_file_name,))
if not os.path.exists(media_filename):
media_filename = None
return "file:///"+media_filename
def get_media_path(media_file_name):
media_filename = get_data_file('media', '%s' % (media_file_name,))
if not os.path.exists(media_filename):
media_filename = None
return "/"+media_filename
class NullHandler(logging.Handler):
def emit(self, record):
pass
def set_up_logging(opts):
# add a handler to prevent basicConfig
root = logging.getLogger()
null_handler = NullHandler()
root.addHandler(null_handler)
formatter = logging.Formatter("%(levelname)s:%(name)s: %(funcName)s() '%(message)s'")
logger = logging.getLogger('uberwriter')
logger_sh = logging.StreamHandler()
logger_sh.setFormatter(formatter)
logger.addHandler(logger_sh)
lib_logger = logging.getLogger('uberwriter_lib')
lib_logger_sh = logging.StreamHandler()
lib_logger_sh.setFormatter(formatter)
lib_logger.addHandler(lib_logger_sh)
# Set the logging level to show debug messages.
if opts.verbose:
logger.setLevel(logging.DEBUG)
logger.debug('logging enabled')
if opts.verbose and opts.verbose > 1:
lib_logger.setLevel(logging.DEBUG)
def get_help_uri(page=None):
# help_uri from source tree - default language
here = os.path.dirname(__file__)
help_uri = os.path.abspath(os.path.join(here, '..', 'help', 'C'))
if not os.path.exists(help_uri):
# installed so use gnome help tree - user's language
help_uri = 'uberwriter'
# unspecified page is the index.page
if page is not None:
help_uri = '%s#%s' % (help_uri, page)
return help_uri
def show_uri(parent, link):
from gi.repository import Gtk # pylint: disable=E0611
screen = parent.get_screen()
Gtk.show_uri(screen, link, Gtk.get_current_event_time())
def alias(alternative_function_name):
'''see http://www.drdobbs.com/web-development/184406073#l9'''
def decorator(function):
'''attach alternative_function_name(s) to function'''
if not hasattr(function, 'aliases'):
function.aliases = []
function.aliases.append(alternative_function_name)
return function
return decorator

View File

@ -1,55 +0,0 @@
# -*- coding:utf-8 -*-
#
# Copyright (C) 2012, Maximilian Köhl <linuxmaxi@googlemail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Python 2/3 unicode
import sys
if sys.version_info.major == 3:
u = lambda x: x
else:
u = lambda x: x.decode('utf-8')
# Metadata
__version__ = '1.1'
__project__ = 'PyLocales'
__short_name__ = 'pylocales'
__authors__ = u('Maximilian Köhl & Carlos Jenkins')
__emails__ = u('linuxmaxi@googlemail.com & carlos@jenkins.co.cr')
__website__ = 'http://pygtkspellcheck.readthedocs.org/'
__source__ = 'https://github.com/koehlma/pygtkspellcheck/'
__vcs__ = 'git://github.com/koehlma/pygtkspellcheck.git'
__copyright__ = u('2012, Maximilian Köhl & Carlos Jenkins')
__desc_short__ = 'Query the ISO 639/3166 database about a country or a language.'
__desc_long__ = ('Query the ISO 639/3166 database about a country or a'
'language. The locales database contains ISO 639 languages'
'definitions and ISO 3166 countries definitions. This package'
'provides translation for countries and languages names if'
'iso-codes package is installed (Ubuntu/Debian).')
__metadata__ = {'__version__' : __version__,
'__project__' : __project__,
'__short_name__' : __short_name__,
'__authors__' : __authors__,
'__emails__' : __emails__,
'__website__' : __website__,
'__source__' : __source__,
'__vcs__' : __vcs__,
'__copyright__' : __copyright__,
'__desc_short__' : __desc_short__,
'__desc_long__' : __desc_long__}
# Should only import Public Objects
from .locales import *

View File

@ -1,138 +0,0 @@
# -*- coding:utf-8 -*-
#
# Copyright (C) 2012, Maximilian Köhl <linuxmaxi@googlemail.com>
# Copyright (C) 2012, Carlos Jenkins <carlos@jenkins.co.cr>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Query the ISO 639/3166 database about a country or a language. The locales
database contains ISO 639 languages definitions and ISO 3166 countries
definitions. This package provides translation for countries and languages names
if iso-codes package is installed (Ubuntu/Debian).
@see utils/locales/build.py to know the database tables and structure.
"""
import gettext
import logging
import os
import sqlite3
# Public Objects
__all__ = ['Country', 'Language', 'LanguageNotFound',
'CountryNotFound', 'code_to_name']
# Translation
_translator_language = gettext.translation('iso_639', fallback=True).gettext
_translator_country = gettext.translation('iso_3166', fallback=True).gettext
# Decides where the database is located. If an application provides an
# os.path.get_module_path monkey patch to determine the path where the module
# is located it uses this. If not it searches in the directory of this source
# code file.
__path__ = None
if hasattr(os.path, 'get_module_path'):
__path__ = os.path.get_module_path(__file__)
if not os.path.isfile(os.path.join(__path__, 'locales.db')):
__path__ = None
if __path__ is None:
__path__ = os.path.abspath(os.path.realpath(os.path.dirname(__file__)))
# Loading the Database
_database = sqlite3.connect(os.path.join(__path__, 'locales.db'))
logger = logging.getLogger(__name__)
# Exceptions
class LanguageNotFound(Exception):
"""
The specified language wasn't found in the database.
"""
class CountryNotFound(Exception):
"""
The specified country wasn't found in the database.
"""
class Country(object):
def __init__(self, rowid):
country = _database.execute('SELECT * FROM countries WHERE rowid == ?', (rowid,)).fetchone()
self.name = country[0]
self.official_name = country[1]
self.alpha_2 = country[2]
self.alpha_3 = country[3]
self.numeric = country[4]
self.translation = _translator_country(self.name)
@classmethod
def get_country(cls, code, codec):
country = _database.execute('SELECT rowid FROM countries WHERE %s == ?' % (codec), (code,)).fetchone()
if country:
return cls(country[0])
raise CountryNotFound('code: %s, codec: %s' % (code, codec))
@classmethod
def by_alpha_2(cls, code):
return Country.get_country(code, 'alpha_2')
@classmethod
def by_alpha_3(cls, code):
return Country.get_country(code, 'alpha_3')
@classmethod
def by_numeric(cls, code):
return Country.get_country(code, 'numeric')
class Language(object):
def __init__(self, rowid):
language = _database.execute('SELECT * FROM languages WHERE rowid == ?', (rowid,)).fetchone()
self.name = language[0]
self.iso_639_2B = language[1]
self.iso_639_2T = language[2]
self.iso_639_1 = language[3]
self.translation = _translator_language(self.name)
@classmethod
def get_language(cls, code, codec):
language = _database.execute('SELECT rowid FROM languages WHERE %s == ?' % (codec), (code,)).fetchone()
if language:
return cls(language[0])
raise LanguageNotFound('code: %s, codec: %s' % (code, codec))
@classmethod
def by_iso_639_2B(cls, code):
return Language.get_language(code, 'iso_639_2B')
@classmethod
def by_iso_639_2T(cls, code):
return Language.get_language(code, 'iso_639_2T')
@classmethod
def by_iso_639_1(cls, code):
return Language.get_language(code, 'iso_639_1')
def code_to_name(code, separator='_'):
"""
Get the natural name of a language based on it's code.
"""
logger.debug('requesting name for code "{}"'.format(code))
code = code.split(separator)
if len(code) > 1:
lang = Language.by_iso_639_1(code[0]).translation
country = Country.by_alpha_2(code[1]).translation
return '{lang} ({country})'.format(lang=lang, country=country)
else:
return Language.by_iso_639_1(code[0]).translation

View File

@ -1,209 +0,0 @@
# -*- encoding: utf-8 -*-
import re
import vim
def cjk_width(text):
import sys
if sys.version_info[0] < 3:
if not isinstance(text, unicode):
text = text.decode("utf-8")
from unicodedata import east_asian_width
return sum(1+(east_asian_width(c) in "WF") for c in text)
def create_separarator(widths, char):
"""Genera una linea para separar filas de una tabla.
El parametro `widths` es un lista indicando el ancho de cada
columna. En cambio el argumento `char` es el caracter que se
tiene que utilizar para imprimir.
El valor retornado es un string.
Por ejemplo::
>>> create_separarator([2, 4], '-')
'+----+------+'
"""
line = []
for w in widths:
line.append("+" + char * (w + 2))
line.append("+")
return ''.join(line)
def create_line(columns, widths):
"""Crea una fila de la tabla separando los campos con un '|'.
El argumento `columns` es una lista con las celdas que se
quieren imprimir y el argumento `widths` tiene el ancho
de cada columna. Si la columna es mas ancha que el texto
a imprimir se agregan espacios vacíos.
Por ejemplo::
>>> create_line(['nombre', 'apellido'], [7, 10])
'| nombre | apellido |'
"""
line = zip(columns, widths)
result = []
for text, width in line:
spaces = " " * (width - cjk_width(text))
text = "| " + text + spaces + " "
result.append(text)
result.append("|")
return ''.join(result)
def create_table(content):
"""Imprime una tabla en formato restructuredText.
El argumento `content` tiene que ser una lista
de celdas.
Por ejemplo::
>>> print create_table([['software', 'vesion'], ['python', '3.2'], ['vim', '7.3']])
+----------+--------+
| software | vesion |
+==========+========+
| python | 3.2 |
+----------+--------+
| vim | 7.3 |
+----------+--------+
"""
# obtiene las columnas de toda la tabla.
columns = zip(*content)
# calcula el tamano maximo que debe tener cada columna.
widths = [max([cjk_width(x) for x in i]) for i in columns]
result = []
result.append(create_separarator(widths, '-'))
result.append(create_line(content[0], widths))
result.append(create_separarator(widths, '='))
for line in content[1:]:
result.append(create_line(line, widths))
result.append(create_separarator(widths, '-'))
return '\n'.join(result)
def are_in_a_table(current_line):
"Indica si el cursor se encuentra dentro de una tabla."
return "|" in current_line or "+" in current_line
def are_in_a_paragraph(current_line):
"Indica si la linea actual es parte de algun parrafo"
return len(current_line.strip()) >= 1
def get_table_bounds(current_row_index, are_in_callback):
"""Obtiene el numero de fila donde comienza y termina la tabla.
el argumento `are_in_callback` tiene que ser una función
que indique si una determinada linea pertenece o no
a la tabla que se quiere medir (o crear).
Retorna ambos valores como una tupla.
"""
top = 0
buffer = vim.current.buffer
max = len(buffer)
bottom = max - 1
for a in range(current_row_index, top, -1):
if not are_in_callback(buffer[a]):
top = a + 1
break
for b in range(current_row_index, max):
if not are_in_callback(buffer[b]):
bottom = b - 1
break
return top, bottom
def remove_spaces(string):
"Elimina los espacios innecesarios de una fila de tabla."
return re.sub("\s\s+", " ", string)
def create_separators_removing_spaces(string):
return re.sub("\s\s+", "|", string)
def extract_cells_as_list(string):
"Extrae el texto de una fila de tabla y lo retorna como una lista."
string = remove_spaces(string)
return [item.strip() for item in string.split('|') if item]
def extract_table(buffer, top, bottom):
full_table_text = buffer[top:bottom]
# selecciona solamente las lineas que tienen celdas con texto.
only_text_lines = [x for x in full_table_text if '|' in x]
# extrae las celdas y descarta los separadores innecesarios.
return [extract_cells_as_list(x) for x in only_text_lines]
def extract_words_as_lists(buffer, top, bottom):
"Genera una lista de palabras para crear una lista."
lines = buffer[top:bottom + 1]
return [create_separators_removing_spaces(line).split('|') for line in lines]
def copy_to_buffer(buffer, string, index):
lines = string.split('\n')
for line in lines:
buffer[index] = line
index += 1
def fix_table(current_row_index):
"""Arregla una tabla para que todas las columnas tengan el mismo ancho.
`initial_row` es un numero entero que indica en que
linea se encuenta el cursor."""
# obtiene el indice donde comienza y termina la tabla.
r1, r2 = get_table_bounds(current_row_index, are_in_a_table)
# extrae de la tabla solo las celdas de texto
table_as_list = extract_table(vim.current.buffer, r1, r2)
# genera una nueva tabla tipo restructured text y la dibuja en el buffer.
table_content = create_table(table_as_list)
copy_to_buffer(vim.current.buffer, table_content, r1)
def FixTable():
(row, col) = vim.current.window.cursor
line = vim.current.buffer[row - 1]
if are_in_a_table(line):
fix_table(row - 1)
else:
print("No estoy en una tabla. Terminando...")
def CreateTable():
(row, col) = vim.current.window.cursor
top, bottom = get_table_bounds(row - 1, are_in_a_paragraph)
lines = extract_words_as_lists(vim.current.buffer, top, bottom)
table_content = create_table(lines)
vim.current.buffer[top:bottom + 1] = table_content.split('\n')

View File

@ -1,69 +0,0 @@
# -*- 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
### DO NOT EDIT THIS FILE ###
__all__ = [
'project_path_not_found',
'get_data_file',
'get_data_path',
]
# Where your project will look for your data (for instance, images and ui
# files). By default, this is ../data, relative your trunk layout
__uberwriter_data_directory__ = '../data/'
__license__ = 'GPL-3'
__version__ = 'VERSION'
import os
from locale import gettext as _
class project_path_not_found(Exception):
"""Raised when we can't find the project directory."""
def get_data_file(*path_segments):
"""Get the full path to a data file.
Returns the path to a file underneath the data directory (as defined by
`get_data_path`). Equivalent to os.path.join(get_data_path(),
*path_segments).
"""
return os.path.join(get_data_path(), *path_segments)
def get_data_path():
"""Retrieve uberwriter data path
This path is by default <uberwriter_lib_path>/../data/ in trunk
and /usr/share/uberwriter in an installed version but this path
is specified at installation time.
"""
# Get pathname absolute or relative.
path = os.path.join(
os.path.dirname(__file__), __uberwriter_data_directory__)
abs_data_path = os.path.abspath(path)
if not os.path.exists(abs_data_path):
raise project_path_not_found
return abs_data_path
def get_version():
return __version__

View File

@ -1,47 +0,0 @@
#!/usr/bin/python3
# -*- 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
### DO NOT EDIT THIS FILE ###
import sys
import os
import locale
locale.textdomain('uberwriter')
# Add project root directory (enable symlink and trunk execution)
PROJECT_ROOT_DIRECTORY = os.path.abspath(
os.path.dirname(os.path.dirname(os.path.realpath(sys.argv[0]))))
python_path = []
if os.path.abspath(__file__).startswith('/opt'):
locale.bindtextdomain('uberwriter', '/opt/extras.ubuntu.com/uberwriter/share/locale')
syspath = sys.path[:] # copy to avoid infinite loop in pending objects
for path in syspath:
opt_path = path.replace('/usr', '/opt/extras.ubuntu.com/uberwriter')
python_path.insert(0, opt_path)
sys.path.insert(0, opt_path)
os.putenv("XDG_DATA_DIRS", "%s:%s" % ("/opt/extras.ubuntu.com/uberwriter/share/", os.getenv("XDG_DATA_DIRS", "/usr/local/share/:/usr/share/")))
if (os.path.exists(os.path.join(PROJECT_ROOT_DIRECTORY, 'uberwriter'))
and PROJECT_ROOT_DIRECTORY not in sys.path):
python_path.insert(0, PROJECT_ROOT_DIRECTORY)
sys.path.insert(0, PROJECT_ROOT_DIRECTORY)
if python_path:
os.putenv('PYTHONPATH', "%s:%s" % (os.getenv('PYTHONPATH', ''), ':'.join(python_path))) # for subprocesses
import uberwriter
uberwriter.main()