#!/usr/bin/env python # -*- coding:UTF-8 -*- # CristianEdit.py por: # Cristian García # # 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 2 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, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import os import ConfigParser import time import datetime import Globales as G from objetos import Menu from objetos import Buffer from objetos import View from objetos import Notebook from objetos import Toolbar from objetos import Navegador from objetos import BarraInferior from objetos import DialogoCerrar from objetos import DialogoEstado from objetos import DialogoReemplazar from objetos import DialogoReemplazarTexto from objetos import Configuraciones from objetos import Teclado from Creditos import Creditos from gi.repository import Gtk from gi.repository import GObject from gi.repository import Pango from gi.repository import Gdk class CristianEdit(Gtk.Plug): """Clase principal.""" __gsignals__ = { 'cerrar': (GObject.SIGNAL_RUN_FIRST, None, []) } def __init__(self): Gtk.Plug.__init__(self) self.buffers = [] self.views = [] self.lugares = [] self.etiquetas = [] self.barras_de_abajo = [] self.configuraciones = {} self.vbox = None self.menu = None self.toolbar = None self.notebook = None self.show_all() def setup_init(self, direcciones): """Se crea la interfaz grafica, se setea y empaqueta todo.""" self.abrir_configuracion() self.vbox = Gtk.VBox() self.menu = Menu(self) hbox_toolbar = Gtk.HBox() self.toolbar = Toolbar() self.notebook = Notebook(self) self.dialogo = DialogoReemplazarTexto(self.get_toplevel()) self.vbox.pack_start(self.menu, False, False, 0) self.vbox.pack_start(hbox_toolbar, False, False, 0) hbox_toolbar.add(self.toolbar) self.vbox.pack_start(self.notebook, True, True, 0) self.notebook.set_show_tabs(False) self.pagina_nueva() self.menu.actualizar_recientes(self.configuraciones['recientes']) numero = 0 for direccion in direcciones: if os.path.exists(direccion): self.abrir(None, direccion=direccion) self.lugares[numero] = direccion numero += 1 self.menu.connect('abrir', self.abrir) self.menu.connect('accion', self.set_accion) self.toolbar.connect('accion', self.set_accion) self.toolbar.connect('buscar', self.buscar_texto) self.notebook.connect('boton-cerrar-clicked', self.cerrar_desde_boton) self.notebook.connect('boton-nuevo-clicked', self.pagina_nueva) self.dialogo.connect('solicitar-buffer', self.setear_variable_buffer) self.add(self.vbox) self.show_all() def mostrar_teclado(self, mostrar): """Muestra un teclado en pantalla.""" if mostrar: ventana = self.get_toplevel() buffer = self.get_buffer() self.teclado = Teclado(ventana, buffer) self.teclado.show_all() else: self.teclado.destroy() def cerrar_desde_boton(self, widget, objeto): """Cierra la pestaña desde el botón de cerrado.""" numero = self.notebook.page_num(objeto) self.notebook.set_current_page(numero) lugar = self.get_direccion() buffer = self.get_buffer() if buffer.get_modified(): dialog = DialogoCerrar(lugar, numero, self.notebook, self.lugares, self.etiquetas, self) respuesta = dialog.run() if respuesta == Gtk.ResponseType.CANCEL: dialog.cerrar(None) elif respuesta == Gtk.ResponseType.NO: self.notebook.remove_page(numero) dialog.cerrar(None) elif respuesta == Gtk.ResponseType.YES: self.guardar(None) self.notebook.remove_page(numero) dialog.cerrar(None) self.lugares = dialog.lugares self.etiquetas = dialog.etiquetas else: self.lugares.remove(self.lugares[numero]) self.buffers.remove(self.buffers[numero]) self.views.remove(self.views[numero]) self.barras_de_abajo.remove(self.barras_de_abajo[numero]) self.etiquetas.remove(self.etiquetas[numero]) self.notebook.remove_page(numero) self.notebook.set_show_tabs(self.notebook.get_n_pages() > 1) def set_accion(self, widget, accion): """Ejecuta una función según la accion recibida.""" if accion == 'Nuevo': self.pagina_nueva() elif accion == 'Abrir': self.abrir() elif accion == 'Guardar': self.guardar() elif accion == 'Guardar Como': self.guardar_como() elif accion == 'Deshacer': self.deshacer() elif accion == 'Rehacer': self.rehacer() elif accion == 'Insertar fecha y hora': self.text_hora() elif accion == 'Estado del archivo': self.estado() elif accion == 'Mostrar teclado': self.mostrar_teclado(True) elif accion == 'Preferencias': self.mostrar_dialogo_configuraciones() elif accion == 'Desplazar al final': self.notebook.desplazar_al_final() elif accion == 'Reemplazar': self.mostrar_dialogo_reemplazar() elif accion == 'Acerca de': Creditos(self.get_toplevel()) def abrir(self, widget=None, direccion=None): """Abrir uno o varios archivo/s.""" pagina = self.notebook.get_current_page() self.menu.bloquear_menus() if not direccion: navegador = Navegador( 'Abrir archivo de Texto', self.get_toplevel(), Gtk.FileChooserAction.OPEN, ['Abrir', Gtk.ResponseType.ACCEPT]) if os.path.exists(self.lugares[pagina]): navegador.set_filename(self.lugares[pagina]) respuesta = navegador.run() if respuesta == Gtk.ResponseType.ACCEPT: for lugar in navegador.get_filenames(): if os.path.exists(lugar) and \ not lugar in self.lugares and \ os.path.isfile(lugar): dir, mod, cuen = self.get_sirve(self.lugares[pagina]) if not dir or not mod: self.pagina_nueva() self.set_accion(None, 'Desplazar al final') pagina = self.notebook.get_current_page() self.lugares[pagina] = lugar buffer = self.get_buffer() view = self.get_view() texto = open(lugar, 'r').read() barra = self.barras_de_abajo[pagina] combo = barra.get_combo() escritura = self.get_escritura(lugar) self.agregar_a_recientes(lugar) self.menu.actualizar_recientes( self.configuraciones['recientes']) buffer.set_text(texto) buffer.set_modified(False) buffer.buscar_lenguaje(lugar, combo) buffer.begin_not_undoable_action() buffer.end_not_undoable_action() start = buffer.get_start_iter() buffer.select_range(start, start) view.set_editable(escritura) nombre = self.lugares[pagina].split('/')[-1] label = self.etiquetas[pagina] label.set_text(nombre) elif lugar in self.lugares: numero = self.lugares.index(lugar) self.notebook.set_current_page(numero) navegador.destroy() else: if os.path.exists(direccion) and \ os.path.isfile(direccion) and \ G.get_mime_type(direccion) and \ not direccion in self.lugares: texto = open(direccion).read() dir, mod, cue = self.get_sirve(self.lugares[pagina]) if not dir or not mod or not cue: self.pagina_nueva() self.set_accion(None, 'Desplazar al final') pagina = self.notebook.get_current_page() buffer = self.get_buffer() barra = self.barras_de_abajo[pagina] combo = barra.get_combo() view = self.get_view() escritura = self.get_escritura(direccion) self.agregar_a_recientes(direccion) self.menu.actualizar_recientes( self.configuraciones['recientes']) buffer.set_text(texto) buffer.set_modified(False) buffer.buscar_lenguaje(direccion, combo) buffer.begin_not_undoable_action() buffer.end_not_undoable_action() view.set_editable(escritura) self.lugares[pagina] = direccion nombre = self.lugares[pagina].split('/')[-1] label = self.etiquetas[pagina] label.set_text(nombre) elif direccion in self.lugares: self.notebook.set_current_page(self.lugares.index(direccion)) self.menu.desbloquear_menus() def guardar(self, widget=None, direccion=None): """Guardar el archivo actual. Si se guarda por primera vez, se llamará a la función 'guardar_como()' para que el usuario seleccione un directorio donde guardar.""" pagina = self.notebook.get_current_page() label = self.etiquetas[pagina] if self.lugares[pagina] == 'Sin dirección' and not direccion: self.guardar_como() else: if not direccion: archivo = open(self.lugares[pagina], 'w') else: archivo = open(direccion, 'w') self.lugares[pagina] = direccion label.set_text(direccion.split('/')[-1]) self.buffers[pagina].set_modified(False) texto = self.get_texto() archivo.write(texto) archivo.close() def guardar_como(self, widget=None): """Guardar copia o por primera vez.""" self.menu.bloquear_menus() pagina = self.notebook.get_current_page() buffer = self.buffers[pagina] self.navegador = Navegador( 'Guardar archivo', self.get_toplevel(), Gtk.FileChooserAction(1), (Gtk.STOCK_SAVE, 1)) respuesta = self.navegador.run() direccion = self.navegador.get_filename() if respuesta == 1: if not os.path.exists(direccion): self.lugares[pagina] = direccion self.guardar(None, direccion=self.lugares[pagina]) self.agregar_a_recientes(direccion) else: DialogoReemplazar( direccion, buffer, self.get_toplevel(), self.get_texto()) texto = self.etiquetas[pagina].get_text() if '/' in texto: self.etiquetas[pagina].set_text(texto.split('/')[-1]) self.navegador.destroy() self.menu.desbloquear_menus() def estado(self, *args): """Muestra un diálogo con los datos del archivo que se está editando actualmente.""" dialogo = DialogoEstado(self, self.notebook) dialogo.show_all() def get_escritura(self, direccion): """Recibe una dirección de un archivo y devuelve sí se puede editar.""" escritura = os.access(direccion, os.W_OK) return escritura def actualizar_b_estado(self, buffer, new_location, mark): """Actualiza la barra de estado, con la posisión actual.""" pagina = self.notebook.get_current_page() b_estado = self.barras_de_abajo[pagina].get_statusbar() b_estado.pop(0) buffer = self.buffers[pagina] iter = buffer.get_iter_at_mark(buffer.get_insert()) linea = iter.get_line() + 1 columna = iter.get_line_offset() + 1 mensage = 'Línea:%d, Columna:%d' % (linea, columna) b_estado.push(0, mensage) def pagina_nueva(self, *args): """Crea algunos Widgets y llama a la función 'agregar' de la Notebook()""" barra_inferior = BarraInferior() combo = barra_inferior.get_combo() buffer = Buffer() view = View(buffer) label = Gtk.Label('Sin título') combo.connect('changed', self.combo_changed) buffer.connect('mark-set', self.actualizar_b_estado) buffer.connect('modified-changed', self.changed_modificado) view.connect('cambio-de-busqueda', self.color_changed) label.modify_font(Pango.FontDescription('Arial')) self.notebook.agregar(view, label, barra_inferior) self.buffers.append(buffer) self.views.append(view) self.lugares.append('Sin dirección') self.etiquetas.append(label) self.barras_de_abajo.append(barra_inferior) view.configurar(self.configuraciones) self.set_accion(None, 'Desplazar al final') self.notebook.set_show_tabs(self.notebook.get_n_pages() > 1) self.show_all() def combo_changed(self, widget): """Usa el lenguaje seleccionado""" pagina = self.notebook.get_current_page() buffer = self.buffers[pagina] nombre = widget.get_active() lenguajes = widget.get_lenguajes() lenguaje_manager = widget.get_lenguaje_manager() if nombre != 0: nombre_lenguaje = lenguajes[nombre -1] lenguaje = lenguaje_manager.get_language(nombre_lenguaje) buffer.set_highlight_syntax(True) buffer.set_language(lenguaje) elif nombre == 0: buffer.set_highlight_syntax(False) def changed_modificado(self, widget): """Hace una acción para llamar la antensión del usuario, y así sabrá cuando un archivo se ha modificado y todaía no se han guardado los cambios""" pagina = self.notebook.get_current_page() label = self.etiquetas[pagina] if widget.get_modified(): label.modify_font(Pango.FontDescription('bold')) else: label.modify_font(Pango.FontDescription('Arial')) def color_changed(self, widget, color): """Cambia el color del texto de la entrada de busqueda, según si lo introducido se encuentre o no.""" entry = self.toolbar.entry entry.modify_fg(Gtk.StateType.NORMAL, Gdk.color_parse(color)) if color == '#FF0000': print '\a' def agregar_a_recientes(self, path): """Agrega una dirección a archivos recientes.""" if os.path.exists(path): if path in self.configuraciones['recientes']: self.configuraciones['recientes'].remove(path) self.configuraciones['recientes'].insert(0, path) self.menu.actualizar_recientes(self.configuraciones['recientes']) self.guardar_configuracion() if len(self.configuraciones['recientes']) > 10: for x in self.configuraciones['recientes'][10:]: self.configuraciones['recientes'].remove(x) def deshacer(self, widget=None): """Deshacer cambios.""" view = self.get_view() view.deshacer() def rehacer(self, widget=None): """Rehacer cambios.""" view = self.get_view() view.rehacer() def text_hora(self, *args): """Inserta la fecha y la hora actual.""" pagina = self.notebook.get_current_page() buffer = self.buffers[pagina] dia = datetime.date.today() fecha = dia.strftime('%d/%m/%y') hora = time.strftime('%H:%M:%S') insertar = buffer.insert_at_cursor(str(fecha + ' ' + hora)) def mostrar_dialogo_reemplazar(self, widget=None): """Muestra un diálogo para reemplzar texto""" if not self.dialogo.mostrado: self.dialogo.mostrado = True texto_actual = '' buffer = self.get_buffer() if buffer.get_selection_bounds(): inicio, fin = buffer.get_selection_bounds() texto_actual = buffer.get_text(inicio, fin, 0) if len(texto_actual.splitlines()) > 1: texto_actual = '' self.dialogo.set_text(texto_actual) self.dialogo.show_all() else: self.dialogo.set_focus(True) def mostrar_dialogo_configuraciones(self, widget=None): """Abre un díalogo con configuraciones.""" ventana = self.get_toplevel() dialog = Configuraciones(ventana, self.configuraciones) dialog.connect('configuration-changed', self.configuration_changed) dialog.show_all() def configuration_changed(self, widget, dict): """Configura todos los views que se encunetren abiertos.""" self.configuraciones = dict self.guardar_configuracion() for view in self.views: view.configurar(self.configuraciones) def get_view(self): """Devuelve el view actual""" pagina = self.notebook.get_current_page() view = self.views[pagina] return view def get_buffer(self): """Devuelve el buffer actual""" pagina = self.notebook.get_current_page() buffer = self.buffers[pagina] return buffer def get_direccion(self): """Devuelve, la dirección acutal""" pagina = self.notebook.get_current_page() lugar = self.lugares[pagina] return lugar def get_fuente(self): return self.configuraciones['fuente'] def get_sirve(self, direccion): """Devuelve si el GtkSourceView() actual sirve para abrir un archivo o si se necesita crear una página nueva en el cuaderno de fichas""" sirve1 = direccion == 'Sin dirección' sirve2 = not self.get_buffer().get_modified() sirve3 = self.get_buffer().get_char_count() == 0 return sirve1, sirve2, sirve3 def get_texto(self): """Obtiene el texto del buffer en la pestaña actual del cuaderno de fichas""" pagina = self.notebook.get_current_page() start, end = self.get_buffer().get_bounds() return self.buffers[pagina].get_text(start, end, 0) def setear_variable_buffer(self, widget): widget.buffer = self.get_buffer() def set_configuracion(self, diccionario): """Establece la configuración según datos de una lista""" self.configuraciones = diccionario for view in self.views: view.configurar(self.configuraciones) self.guardar_configuracion() def get_objeto(self, lista): """Devuelve el objeto solicitado""" return eval(lista) def buscar_texto(self, widget, accion, texto): """Busca texto.""" if accion == 'Buscar activate': self.buscar_enter(None, texto) if accion == 'Buscar changed': self.buscar_changed(None, texto) def buscar_changed(self, widget, texto): """Busca texto en el buffer actual por cada cambio que se haga en la entrada de texto""" view = self.get_view() view.buscar_texto(texto) def buscar_enter(self, widget, texto): """Busca texto en el buffer actual y selecciona la siguiente coincidecia""" view = self.get_view() view.buscar_texto(texto, enter=True) def guardar_configuracion(self, *args): """Guarda la configuración en el archivo 'configuracion.cfg'""" cfg = ConfigParser.ConfigParser() cfg.add_section('datos') cfg.set('datos', 'enumeracion', self.configuraciones['enumeracion']) cfg.set('datos', 'fuente', self.configuraciones['fuente']) cfg.set('datos', 'margen', self.configuraciones['margen']) cfg.set('datos', 'is_margen', self.configuraciones['is_margen']) cfg.set('datos', 'ajuste', self.configuraciones['ajuste']) cfg.set('datos', 'ajuste_palabras', self.configuraciones['ajuste_palabras']) cfg.set('datos', 'tabulador', self.configuraciones['tabulador']) cfg.set('datos', 'insertar_espacios', self.configuraciones['insertar_espacios']) cfg.set('datos', 'sangria', self.configuraciones['sangria']) cfg.set('datos', 'tema', self.configuraciones['tema']) cfg.set('datos', 'recientes', str(self.configuraciones['recientes'])) f = open(G.configuracion, 'w') cfg.write(f) f.close() def abrir_configuracion(self, *args): """Abre la configuración desde el archivo configuraciones.cfg""" cfg = ConfigParser.ConfigParser() cfg.read([G.configuracion]) try: try: self.configuraciones['enumeracion'] = eval( cfg.get('datos', 'enumeracion')) self.configuraciones['fuente'] = str(cfg.get('datos', 'fuente')) self.configuraciones['margen'] = int(cfg.get('datos', 'margen')) self.configuraciones['is_margen'] = eval( cfg.get('datos', 'is_margen')) self.configuraciones['ajuste'] = eval( cfg.get('datos', 'ajuste')) self.configuraciones['ajuste_palabras'] = eval( cfg.get('datos', 'ajuste_palabras')) self.configuraciones['tabulador'] = int(eval( cfg.get('datos', 'tabulador'))) self.configuraciones['insertar_espacios'] = eval( cfg.get('datos', 'insertar_espacios')) self.configuraciones['sangria'] = eval(cfg.get( 'datos', 'sangria')) self.configuraciones['recientes'] = eval( cfg.get('datos', 'recientes')) self.configuraciones['tema'] = str(cfg.get('datos', 'tema')) except: self.crear_configuraciones() except ConfigParser.NoOptionError: self.crear_configuraciones() def crear_configuraciones(self, *args): """Crea el diccionario de configuraciones y lo guarda.""" self.configuraciones = { 'enumeracion': False, 'margen': 80, 'is_margen': False, 'ajuste': True, 'ajuste_palabras': True, 'tabulador': 8, 'insertar_espacios': False, 'sangria': False, 'tema': 'classic', 'fuente': 'Monospace', 'recientes': [], 'abrir_archivo': False} self.guardar_configuracion()