/tags/pyragua-0.2.5/pyragua/Archivo.py – Pyragua

root/tags/pyragua-0.2.5/pyragua/Archivo.py

Revision 390, 16.6 KB (checked in by ark, 2 years ago)

directorio faltante

Line 
1# -*- coding: iso8859-1 -*-
2"""
3Este archivo es parte de Pyragua
4
5Pyragua es software libre; lo puedes redistribuir y/o modificar
6bajo los terminos de la Licencia Publica General (GNU GPL) como fue
7publicada por la Free Software Foundation; cualquier version 2 de la
8Licencia.
9
10Este programa es distribuido con la esperanza de que ser útil,
11pero SIN GARANTIA ALGUNA; ni con la garantía explícita de
12MERCABILIDAD o de que SERVIRA PARA UN PROPOSITO EN PARTICULAR.
13Mire la Licencia Pública General de la GNU para más detalles.
14
15Debió recibir una copia de la Licencia Pública General de la GNU junto con
16este programa; sino, escriba a la Free Software Foundation,
17Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18"""
19DEBUG=False
20
21import wx.stc  as  stc
22import wx
23import sys
24import os
25import os.path
26#Para la ejecucción
27import popen2
28# Para i18n
29import gettext
30gettext.install("pyragua",unicode=1)
31_ = gettext.gettext
32
33from PythonSTC import PythonSTC
34from Utils import *
35
36class Archivo(wx.Panel):
37    #TODO que esto sea parametrizable
38    codificacion='iso8859-1'
39#    codificacion = 'utf-8'
40    """Esta clase va a representar un archivo fuente abierto en el editor"""
41    def __init__(self,nombre,padre):
42        """El constructor, recibe el nombre del archivo a abrir y una
43        referencia al Notebook padre"""
44        wx.Panel.__init__(self,padre,-1, size=(80,80))
45        self.padre=padre
46        self.pyragua=self.padre.pyragua
47        self.nombre=os.path.abspath(nombre)
48        self.BusquedaActiva = False # Esto es para combrobar si hay una busqueda
49        #Esta variable me indica si se ha modificado una página
50        self.HideLines=[]
51        self.modificado=False
52        #el editor
53        self.stcEditor = PythonSTC(self, -1)
54        #Si el nombre es vacio es porque es un archivo nuevo
55        if len(nombre)>0:
56            try:
57                arch=open(nombre, 'rb')
58                txt=arch.read()
59                #Si es None debo tomar la codificación por defecto del sistema
60                #if arch.encoding:
61                #    self.codificacion=arch.encoding
62                #else:
63                #    self.codificacion=sys.getdefaultencoding()
64                #   #Codificación
65                #    txt=txt.decode(self.codificacion)
66                #    arch.close()
67            except:
68                #Falló la carga desde archivo
69                Utils.MostrarError("Error cargando el archivo, usando el texto por defecto")
70                txt=self.TextoNuevo()
71        else :
72            #El archivo es nuevo
73            txt=self.TextoNuevo()
74        #Cargo el contenido del archivo en el editor
75
76        txt = txt.decode('latin-1') 
77        self.stcEditor.SetText(txt)
78
79        self.Layout()
80        self.Propiedades()
81        eol=self.IdentificarEOL()
82        if eol!=-1:
83            self.stcEditor.TIPO_EOL=eol
84            self.stcEditor.SetEOLMode(eol)
85
86        if DEBUG: print "FIN constructor archivo"
87
88
89    def TextoNuevo(self):
90        '''Retorna el texto que se incluye en los archivos nuevos'''
91        import time
92        now = time.localtime(time.time())
93        if wx.Platform == '__WXMSW__':
94            #Probado en WINXP
95            login=os.environ['USERNAME']
96        else:
97            #TODO sería bueno verificar que esté en GNU/Linux, ya que este texto
98            #solo ha sido probado en esa plataforma
99            login=os.environ['USER']
100        if self.stcEditor.TIPO_EOL==stc.STC_EOL_CRLF:
101            txt='#! -*- coding: %s -*-\r\n#Nuevo Archivo.\r\n#Creado por %s.\r\n#Creado: %s\r\n'
102        elif self.stcEditor.TIPO_EOL==stc.STC_EOL_CR:
103            txt='#! -*- coding: %s -*-\r#Nuevo Archivo.\r#Creado por %s.\r#Creado: %s\r'
104        elif self.stcEditor.TIPO_EOL==stc.STC_EOL_LF:
105            txt='#! -*- coding: %s -*-\n#Nuevo Archivo.\n#Creado por %s.\n#Creado: %s\n'
106        txt=txt%(self.codificacion,login, time.asctime(now))
107        return txt
108
109
110    def Propiedades(self):
111        """Establece propiedades especiales de los widgets"""
112        #self.stcEditor.SetText(u"háslña")
113        self.stcEditor.EmptyUndoBuffer()
114        self.stcEditor.Colourise(0, -1)
115
116        # line numbers in the margin
117        self.stcEditor.SetMarginType(1, stc.STC_MARGIN_NUMBER)
118        self.stcEditor.SetMarginWidth(1, 25)
119
120    def Layout(self):
121        """Organiza la ventana en sizers"""
122        sArchivo=wx.BoxSizer(wx.HORIZONTAL)
123        self.sArchivo=sArchivo
124        sArchivo.Add(self.stcEditor,1,wx.EXPAND,0)
125        self.SetSizer(sArchivo)
126        self.SetAutoLayout(True)
127        sArchivo.Layout()
128
129    def ComprobarConsola(self):
130        """Este método comprueba la existencia en el sistema GNU/LINUX la
131        existencia de una consola (xterm, gnome-terminal) adecuada para ejecutar el programa"""
132        consola=""
133        for con in ["xterm","konsole","gnome-terminal"]:
134            ruta=popen2.popen2("which %s"%con)[0].readline()
135            ruta=EliminarEOLS(ruta)
136            if os.path.isfile(ruta):
137                consola=con
138                break
139        #Parámetros especiales de cada consola
140        if consola=="xterm":
141            return "%s -T Pyragua -hold -e"%ruta
142        elif consola=="konsole":
143            return "%s  -T Pyragua -e"%ruta #le quito el --noclose
144        elif consola=="gnome-terminal":
145            return "%s -t Pyragua -e"%ruta
146        else:
147            #No encontré consola
148            return ""
149
150
151    def Ejecutar(self,evento=None):
152        """Ejecuta el archivo, recibe el evento que lo lanzó"""
153        #guardamos primero
154        salida,info=self.OnGuardar(evento)
155        if not salida:
156            return False,"Error guardando\n%s"%info
157        #Selecciono la consola
158        if wx.Platform == '__WXMSW__':
159            #Si estamos en windows, mandamos a ejecutar directamente
160            #sout,sin=popen2.popen2('''%s "%s" '''%(sys.executable,self.nombre))
161            os.popen('start "%s" "%s"'%(sys.executable,self.nombre))
162            #os.popen('start "%s" "%s"'%(sys.executable,self.nombre))
163        else:
164            #En otra plataforma dependemos del xterm
165            consola=self.ComprobarConsola()
166            if consola=="":
167                MostrarError(self.padre,_(u"No puedo encontrar una consola, o no están en el path, por favor instalalas (xterm, konsole o gnome-terminal)"))
168            #sout,sin=popen2.popen2('''%s "%s" '''%(sys.executable,self.nombre))
169            os.popen(consola+''' "%s" "%s" '''%(sys.executable,self.nombre))
170
171
172    def OnGuardar(self,evento=None):
173        """Esta función solo revisa si no se ha guardado todavía
174        ningúna vez y llama a GuardarComo, de lo contrario llama a
175        guardar"""
176        #Es un archivo nuevo
177        if len(self.nombre)==0:
178            salida,msg=self.GuardarComo()
179            if not salida:
180                #Un error guardando
181                return False,msg
182            else:
183                return True, ""
184        else:
185            #Llamo el verdadero guardar
186            return self.Guardar()
187
188    def GuardarComo(self,evento=None):
189        """Le permite al usuario seleccionar que nombre de archivo
190        desea, luego llama a guardar"""
191        dlg=wx.FileDialog(self,"Seleccione un archivo", os.getcwd(),
192                          defaultFile="",
193                          wildcard="*.py",
194                          style=wx.SAVE | wx.CHANGE_DIR|wx.OVERWRITE_PROMPT )
195        salida = dlg.ShowModal()
196        if salida == wx.ID_OK:
197            #Seleccionaron bien el archivo
198            paths=dlg.GetPaths()
199            self.nombre=paths[0]
200            #Con el nuevo nombre establecido llamo a guardar
201            return self.Guardar()
202        else:
203            #Ojo retornamos siempre el estado y luego la información
204            if salida == wx.ID_CANCEL:
205                return False,"Cancelado"
206            return False,""
207
208
209
210    def Guardar(self,evento=None):
211        """Pasa los datos del stc al archivo físico"""
212        try:
213            arch=open(self.nombre,"wb")
214        except:
215            #Error guardando el archivo
216            info=sys.exc_info()[:][1]
217            if DEBUG: print 'Error guardando el archivo'+str(info)
218            #Retornemos Falso y la descripción del error
219            return False,info
220
221        texto=self.stcEditor.GetText()
222        try:
223            #Esta línea se necesita porque o si no no guarda
224            #las tíldes
225
226            if type(texto)==type(u"unicode"):
227                texto=texto.encode(self.codificacion)
228            arch.write(texto)
229        except:
230            #Error guardando el archivo
231            #La info de la exepción, hay que sacar una copia por recomendación
232            # de la docu oficial de python
233            info=sys.exc_info()[:][1]
234            if DEBUG: print 'error en el write con la codificacion '+str(info)
235            #Retornemos Falso y la descripción del error
236            return False,info
237        arch.close()
238        #Con esto sabremos cuando lo han modificado
239        self.stcEditor.SetSavePoint()
240        #Cambiamos el nombre de la página para que no tenga el * de modificado
241        self.padre.SetPageText(self.padre.GetSelection(),
242                               str(self.padre.GetSelection()+1)+' '+os.path.basename(self.nombre))
243        return True, ""
244
245
246
247    def GotoLine ( self ):
248        """Este metodo sirve para ir a una ubicacion espesifica dada por el usuario"""
249        dlg=wx.TextEntryDialog(self,_("Escribe el numero de linea a la cual deseas ir:"),_("Ir a la linea"))
250        if dlg.ShowModal()==wx.ID_OK:
251            try:
252                numero_de_linea=int(dlg.GetValue())-1
253                if numero_de_linea<self.stcEditor.GetLineCount() :
254                    #Expande las cabeceras en caso de ser necesario
255                    self.stcEditor.EnsureVisible(numero_de_linea)
256                    #LineasEscondidas= self.GetHideLines(self.stcEditor.GetCurrentLine())
257                    #self.stcEditor.ScrollToLine(self.stcEditor.GetCurrentLine()-LineasEscondidas)
258                    self.stcEditor.GotoLine(numero_de_linea)
259                else:
260                    wx.MessageBox(_(u"El numero introducido es mayor que el numero de lineas del archivo"))
261            except:
262                wx.MessageBox(_(u"El numero introducido no es valido"))
263
264    def BuscarTexto(self,evento):
265        """Este metodo controla que el lo que debe hacer el buscar"""
266        self.BusquedaActiva = True # Garantizo que ya hay una busqueda
267        TipoEvento = evento.GetEventType()
268        self.Busqueda = evento.GetFindString()
269        self.Flags = evento.GetFlags()
270        # evento Buscar
271        if TipoEvento in [wx.wxEVT_COMMAND_FIND]:
272            #El stcEditor.FindText retorna la start position de la cadena encontrad, sino -1 que no fue encontrada
273            StartPosicion=self.stcEditor.FindText(self.stcEditor.GetCurrentPos(),
274                                            self.stcEditor.GetLineEndPosition(self.stcEditor.GetLineCount()),
275                                            self.Busqueda,self.Flags) # Esto define de donde a donde va a buscar
276            if StartPosicion !=-1:
277                self.stcEditor.SetSelection(StartPosicion,StartPosicion+len(self.Busqueda))
278                self.stcEditor.ShowLines(1,self.stcEditor.GetLineCount())
279                self.stcEditor.EnsureVisible(self.stcEditor.GetCurrentLine())
280                LineasEscondidas= self.GetHideLines(self.stcEditor.GetCurrentLine())
281                self.stcEditor.ScrollToLine(self.stcEditor.GetCurrentLine()-LineasEscondidas)
282            else:
283                MostrarError(self.padre,_(u"La cadena no pudo ser encontrada"))
284
285
286        # evento Buscar Siguiente
287        elif TipoEvento in [wx.wxEVT_COMMAND_FIND_NEXT]:
288            pos =self.stcEditor.GetSelectionEnd()
289            self.stcEditor.SetCurrentPos(pos)
290            self.stcEditor.GotoPos(pos)
291            self.stcEditor.SearchAnchor()# El stc recomienda llamar este metodo antes de llamar a SearchNext
292            if self.Flags%2 :
293                if self.stcEditor.SearchNext(self.Flags,self.Busqueda) == -1:
294                    if MostrarAviso(self.padre,_(u"Ha llegado al final del documento desea volver al principio")):
295                        self.stcEditor.GotoPos(0)
296            else:
297                print self.stcEditor.GetSelectedText()
298                if self.stcEditor.GetSelectedText():
299                    self.stcEditor.SetCurrentPos(self.stcEditor.GetSelectionStart())
300                    print self.stcEditor.GetSelectionStart()
301                if self.stcEditor.SearchPrev(self.Flags,self.Busqueda)==-1:
302                    if MostrarAviso(self.padre,_(u"Ha llegado al principio del documento desea volver al final")):
303                        self.stcEditor.GotoPos(self.stcEditor.GetLineEndPosition(self.stcEditor.GetLineCount()))
304            self.stcEditor.ShowLines(1,self.stcEditor.GetLineCount())
305            self.stcEditor.EnsureVisible(self.stcEditor.GetCurrentLine())
306            LineasEscondidas= self.GetHideLines(self.stcEditor.GetCurrentLine())
307            self.stcEditor.ScrollToLine(self.stcEditor.GetCurrentLine()-LineasEscondidas)
308
309
310        # evento Buscar Remplazar
311        elif TipoEvento in [wx.wxEVT_COMMAND_FIND_REPLACE]:
312            if not self.stcEditor.GetSelectedText(): # debuelve 0 si no hay nada selecionado
313                self.stcEditor.SearchAnchor()# El stc recomienda llamar este metodo antes SearchNext
314                self.stcEditor.SearchNext(self.Flags,self.Busqueda)
315            else:
316                if self.stcEditor.GetSelectedText() == self.Busqueda:
317                    RemplaceString = evento.GetReplaceString()
318                    self.stcEditor.ReplaceSelection(RemplaceString)
319                self.stcEditor.SearchAnchor()# El stc recomienda llamar este metodo antes SearchNext
320                if self.stcEditor.SearchNext(self.Flags,self.Busqueda) == -1:
321                    self.stcEditor.GotoPos(0)
322            self.stcEditor.ShowLines(0,self.stcEditor.GetLineCount())
323            self.stcEditor.EnsureVisible(self.stcEditor.GetCurrentLine())
324            LineasEscondidas= self.GetHideLines(self.stcEditor.GetCurrentLine())
325            self.stcEditor.ScrollToLine(self.stcEditor.GetCurrentLine()-LineasEscondidas)
326
327        # evento Buscar Remplazar Todo
328        elif TipoEvento in [wx.wxEVT_COMMAND_FIND_REPLACE_ALL]:
329            self.stcEditor.GotoPos(1)
330            RemplaceString = evento.GetReplaceString()
331            while  1 :
332                self.stcEditor.SearchAnchor()
333                if self.stcEditor.SearchNext(self.Flags,self.Busqueda) == -1:
334                    break
335                self.stcEditor.ReplaceSelection(RemplaceString)
336
337    def GetHideLines ( self , pos):
338        """Retorna el numero de lineas escondidad que hay hasta una posicion dada """
339        HideLines=0
340        linea=0
341        while linea<pos:
342            if not self.stcEditor.GetLineVisible(linea) :
343                HideLines+=1
344            if DEBUG and not self.stcEditor.GetLineVisible(linea) :print (linea+1, pos+1), "Hide line"
345            if DEBUG and  self.stcEditor.GetLineVisible(linea):print (linea+1, pos+1), "visible line"
346            linea+=1
347        return HideLines
348
349    def PreguntarGuardar(self):
350        """Le pregunta al usuario si quiere que guarde el archivo
351        porque aún no lo ha hecho"""
352        dlg=wx.MessageDialog(self.padre,
353                             u"El archivo %s no ha sido guardado aún, desea guardarlo ahora?"%self.nombre,
354                             u"El archivo no se ha grabado aún",
355                             wx.YES_NO|wx.ICON_QUESTION)
356
357        if dlg.ShowModal()==wx.ID_YES:
358            self.OnGuardar()
359
360    def CambiarEOL(self,eol):
361        """Evento que cambia el tipo de fin de línea del archivo actual"""
362        self.stcEditor.TIPO_EOL=eol
363        self.stcEditor.SetEOLMode(eol)
364        if DEBUG: print "Cambiar EOL", eol, stc.STC_EOL_CRLF, stc.STC_EOL_LF, stc.STC_EOL_CR
365
366    def IdentificarEOL(self):
367        """Este método se encargará de retornar el tipo de EOL que utiliza un archivo"""
368        txt=self.stcEditor.GetText()
369        eol=stc.STC_EOL_CRLF
370
371        if txt=="" :
372            return stc.STC_EOL_CRLF
373        #Busco el primer salto de línea que haya
374        for i,c in enumerate(txt):
375            #if DEBUG: print "Archivo: IdentificarEOL","c", c,ord(c)
376            if c=='\n' or c=='\r':
377                break
378        if c not in ['\n','\r']:
379            #No encontré el salto de línea,
380            if DEBUG: print "Archivo: IdentificarEOL no encontre EOL"
381            return -1
382        #Miremos el siguiente caracter para saber si es CRLF
383        if i>(len(txt)-1):
384            siguiente=""
385        else:
386            siguiente=txt[i+1]
387
388        if c=='\r' and siguiente=='\n':
389            #CRLF
390            if DEBUG: print "EOL Windows"
391            return stc.STC_EOL_CRLF
392        elif c=='\r':
393            if DEBUG: print "EOL MAC"
394            return stc.STC_EOL_CR
395        elif c=='\n':
396            if DEBUG: print "EOL Linux"
397            return stc.STC_EOL_LF
Note: See TracBrowser for help on using the browser.