| 1 | #-*- coding:iso8859-1 -*- |
|---|
| 2 | """ |
|---|
| 3 | Este archivo es parte de Pyragua |
|---|
| 4 | |
|---|
| 5 | Pyragua es software libre; lo puedes redistribuir y/o modificar |
|---|
| 6 | bajo los terminos de la Licencia Publica General (GNU GPL) como fue |
|---|
| 7 | publicada por la Free Software Foundation; cualquier versión 2 de la |
|---|
| 8 | Licencia. |
|---|
| 9 | |
|---|
| 10 | Este programa es distribuido con la esperanza de que será útil, |
|---|
| 11 | pero SIN GARANTIA ALGUNA; ni con la garantía explícita de |
|---|
| 12 | MERCABILIDAD o de que SERVIRA PARA UN PROPOSITO EN PARTICULAR. |
|---|
| 13 | Mire la Licencia Pública General de la GNU para más detalles. |
|---|
| 14 | |
|---|
| 15 | Debió recibir una copia de la Licencia Pública General de la GNU junto con |
|---|
| 16 | este programa; sino, escriba a la Free Software Foundation, |
|---|
| 17 | Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|---|
| 18 | """ |
|---|
| 19 | |
|---|
| 20 | from Archivo import Archivo |
|---|
| 21 | import wx |
|---|
| 22 | import os.path as path |
|---|
| 23 | import os |
|---|
| 24 | import sys |
|---|
| 25 | # Para i18n |
|---|
| 26 | import gettext |
|---|
| 27 | gettext.install("pyragua",unicode=1) |
|---|
| 28 | _ = gettext.gettext |
|---|
| 29 | |
|---|
| 30 | class PanelArchivos(wx.Panel): |
|---|
| 31 | def __init__(self,*args,**kwargs): |
|---|
| 32 | self.padre=kwargs['padre'] |
|---|
| 33 | self.vInicial=self.padre |
|---|
| 34 | self.pyragua=self.vInicial.pyragua |
|---|
| 35 | del kwargs['padre'] |
|---|
| 36 | |
|---|
| 37 | wx.Panel.__init__(self,*args,**kwargs) |
|---|
| 38 | self.nArchivos=wx.Notebook(self, -1,style =wx.BORDER_SUNKEN) |
|---|
| 39 | self.nArchivos.pyragua=self.pyragua |
|---|
| 40 | #La lista de los archivos |
|---|
| 41 | self.lArchivos=[] |
|---|
| 42 | self.PathArchivos=[] |
|---|
| 43 | self.Layout() |
|---|
| 44 | |
|---|
| 45 | self.nArchivos.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED,self.OnCambiarPagina) |
|---|
| 46 | self.nArchivos.Bind(wx.EVT_RIGHT_UP,self.PopUpPestana) |
|---|
| 47 | |
|---|
| 48 | |
|---|
| 49 | def Layout(self): |
|---|
| 50 | """Se encarga de posicionar los sizer""" |
|---|
| 51 | sArchivos=wx.BoxSizer(wx.VERTICAL) |
|---|
| 52 | self.sArchivos=sArchivos |
|---|
| 53 | sArchivos.Add(self.nArchivos,1,wx.EXPAND,0) |
|---|
| 54 | self.SetSizer(sArchivos) |
|---|
| 55 | self.SetAutoLayout(True) |
|---|
| 56 | sArchivos.Layout() |
|---|
| 57 | |
|---|
| 58 | def AnunciarUser(self,Posicion,hilo): |
|---|
| 59 | """""" |
|---|
| 60 | dlg = wx.MessageDialog(self, _(u'EL archivo '+self.nArchivos.GetPageText(Posicion).split(' ')[1] +' sido modificado en otra parte,\n Desea recargarlo'), |
|---|
| 61 | _(u'Aviso'), |
|---|
| 62 | #wx.OK | wx.ICON_INFORMATION |
|---|
| 63 | wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION |
|---|
| 64 | ) |
|---|
| 65 | if dlg.ShowModal()==wx.ID_YES: |
|---|
| 66 | codificacion='iso8859-1' |
|---|
| 67 | arch=open(self.PathArchivos[Posicion]) |
|---|
| 68 | txt=arch.read() |
|---|
| 69 | txt=txt.decode(codificacion) |
|---|
| 70 | arch.close() |
|---|
| 71 | self.nArchivos.SetSelection(Posicion) |
|---|
| 72 | pag= self.lArchivos[Posicion] |
|---|
| 73 | pag.stcEditor.ClearAll() |
|---|
| 74 | pag.stcEditor.SetText(txt) |
|---|
| 75 | self.nArchivos.SetPageText(Posicion, str(self.nArchivos.GetSelection()+1)+' '+self.nArchivos.GetPageText(Posicion).split(' ')[1]+' '+'*') |
|---|
| 76 | #pag.stcEditor.ConvertEOLs(self.stcEditor.TIPO_EOL) |
|---|
| 77 | hilo.ActualizarModificaciones(self.PathArchivos[Posicion],Posicion) |
|---|
| 78 | dlg.Destroy() |
|---|
| 79 | |
|---|
| 80 | def AnalizarArchivo(self): |
|---|
| 81 | """Analiza los fines de linea de un archivo y si hay errores muestra posibles soluciones""" |
|---|
| 82 | NumTab=0 |
|---|
| 83 | NumEOL_CR=NumEOL_LF=NumEOL_CRLF=0 |
|---|
| 84 | pag=self.nArchivos.GetCurrentPage() |
|---|
| 85 | TabWidth=pag.stcEditor.GetTabWidth() |
|---|
| 86 | arch=open(pag.nombre) |
|---|
| 87 | for linea in arch.readlines(): |
|---|
| 88 | Cont=0 |
|---|
| 89 | for palabra in linea: |
|---|
| 90 | Cont+=1 |
|---|
| 91 | if palabra==' ': |
|---|
| 92 | NumTab+=1 |
|---|
| 93 | if palabra in ['\n','\r']: |
|---|
| 94 | if len(linea)!=Cont: |
|---|
| 95 | NumEOL_CRLF+=1 |
|---|
| 96 | break |
|---|
| 97 | elif palabra in ['\n']: |
|---|
| 98 | NumEOL_LF+=1 |
|---|
| 99 | continue |
|---|
| 100 | elif palabra in ['\r']: |
|---|
| 101 | NumEOL_CR+=1 |
|---|
| 102 | continue |
|---|
| 103 | arch.close() |
|---|
| 104 | if pag.stcEditor.GetUseTabs(): |
|---|
| 105 | MsgTab='Es archivo esta usando Tab' |
|---|
| 106 | else: |
|---|
| 107 | if NumTab: |
|---|
| 108 | MsgTab='El archivo esta usando una combinacion de '+str(NumTab)+' Tab y espacios' |
|---|
| 109 | else: |
|---|
| 110 | MsgTab='No esta usando Tabs' |
|---|
| 111 | ListaFinLinea=[] |
|---|
| 112 | ListaFinLinea.extend([NumEOL_CR,NumEOL_CRLF,NumEOL_LF]) |
|---|
| 113 | ListaFinLinea.sort() # organizo la lista |
|---|
| 114 | # Miramos en que plataforma esta corriendo |
|---|
| 115 | if wx.Platform == '__WXMSW__': |
|---|
| 116 | Plataforma='Win' |
|---|
| 117 | elif wx.Platform == '__WXMAC__': |
|---|
| 118 | Plataforma= 'Mac' |
|---|
| 119 | else: |
|---|
| 120 | Plataforma='Linux' |
|---|
| 121 | # Ahora miramos que si el achivo usa solo un fin de linea |
|---|
| 122 | if not (ListaFinLinea[0]==0 and ListaFinLinea[1]==0): |
|---|
| 123 | MsgNumEOL='Esta usando diversos tipos de linea en el docunemto recomendamos que solo use uno' |
|---|
| 124 | else: |
|---|
| 125 | if ListaFinLinea[2]==NumEOL_CRLF: |
|---|
| 126 | if Plataforma == 'Win': |
|---|
| 127 | MsgNumEOL='' |
|---|
| 128 | else: |
|---|
| 129 | MsgNumEOL='Esta usando fines de linea que no son propios de la arquitectura en la cual trabaja' |
|---|
| 130 | if ListaFinLinea[2]==NumEOL_CR: |
|---|
| 131 | if Plataforma == 'Mac': |
|---|
| 132 | MsgNumEOL='' |
|---|
| 133 | else: |
|---|
| 134 | MsgNumEOL='Esta usando fines de linea que no son propios de la arquitectura en la cual trabaja' |
|---|
| 135 | if ListaFinLinea[2]==NumEOL_LF: |
|---|
| 136 | if Plataforma == 'Linux': |
|---|
| 137 | MsgNumEOL='' |
|---|
| 138 | else: |
|---|
| 139 | MsgNumEOL='Esta usando fines de linea que no son propios de la arquitectura en la cual trabaja' |
|---|
| 140 | print NumEOL_CR, NumEOL_LF,NumEOL_CRLF |
|---|
| 141 | if MsgNumEOL=='': |
|---|
| 142 | Msg='Se ha detectado en el siguiente archivo:\n- '+MsgTab+'\nQue desea hacer:' |
|---|
| 143 | else: |
|---|
| 144 | Msg='Se ha detectado en el siguiente archivo:\n- '+MsgTab+'\n- '+MsgNumEOL+'\nQue desea hacer:' |
|---|
| 145 | dlg = wx.SingleChoiceDialog( |
|---|
| 146 | self, Msg ,'Aviso', |
|---|
| 147 | ['uno','dos'], |
|---|
| 148 | wx.CHOICEDLG_STYLE |
|---|
| 149 | ) |
|---|
| 150 | |
|---|
| 151 | if dlg.ShowModal() == wx.ID_OK: |
|---|
| 152 | Seleccion=dlg.GetStringSelection() |
|---|
| 153 | |
|---|
| 154 | dlg.Destroy() |
|---|
| 155 | |
|---|
| 156 | def AgregarNuevoArchivo ( self , nombre): |
|---|
| 157 | open(nombre, "wb").close() |
|---|
| 158 | self.AgregarArchivo(nombre) |
|---|
| 159 | |
|---|
| 160 | def AgregarArchivo(self,nombre): |
|---|
| 161 | """Recibe el nombre de un archivo y lo abre en un stc, luego lo mete dentro del panel""" |
|---|
| 162 | |
|---|
| 163 | # Comprueva si el archivo esta abierto y si lo esta se sale del metodo |
|---|
| 164 | for i in self.PathArchivos: |
|---|
| 165 | if i == '': |
|---|
| 166 | continue |
|---|
| 167 | else: |
|---|
| 168 | if i == nombre: |
|---|
| 169 | self.nArchivos.SetSelection(self.PathArchivos.index(i)) |
|---|
| 170 | return |
|---|
| 171 | self.PathArchivos.append(nombre) |
|---|
| 172 | pArchivo=self.vInicial.dProyectos['default'].AgregarArchivo(nombre, padre=self.nArchivos) |
|---|
| 173 | self.lArchivos.append(pArchivo) |
|---|
| 174 | num =str(self.nArchivos.GetPageCount()+1) |
|---|
| 175 | if len(nombre)==0: |
|---|
| 176 | #Un archivo nuevo |
|---|
| 177 | self.nArchivos.AddPage(pArchivo,num+' '+_('Nuevo'),select = True) |
|---|
| 178 | else : |
|---|
| 179 | self.nArchivos.AddPage(pArchivo,num+' '+path.basename(nombre),select = True) |
|---|
| 180 | #Copio la coficación en la barra de estado |
|---|
| 181 | self.padre.sBar.SetStatusText(pArchivo.codificacion,1) |
|---|
| 182 | |
|---|
| 183 | #Dejo el cursor listo para empezar a escribir |
|---|
| 184 | pArchivo.stcEditor.SetFocus() |
|---|
| 185 | if len(nombre)!=0: |
|---|
| 186 | self.pyragua.cb.AddFile(nombre, open(nombre).readlines()) |
|---|
| 187 | self.pyragua.finicial.pCodigo.aCodigo.CambiarArchivo(nombre) |
|---|
| 188 | |
|---|
| 189 | def Cerrar(self,evento): |
|---|
| 190 | """Este metodo cierra la pestaña y debuelve el numero del la pestaña que se esta cerrando""" |
|---|
| 191 | #Miro si hay archivos para cerrar |
|---|
| 192 | if self.nArchivos.GetPageCount()>0: |
|---|
| 193 | pag=self.nArchivos.GetCurrentPage() |
|---|
| 194 | num=self.nArchivos.GetSelection() |
|---|
| 195 | pag.stcEditor.EmptyUndoBuffer() |
|---|
| 196 | #self.pyragua.cb.DelFile(pag.nombre) |
|---|
| 197 | self.pyragua.finicial.pCodigo.aCodigo.EliminarArchivo(pag.nombre) |
|---|
| 198 | self.nArchivos.DeletePage(num) |
|---|
| 199 | self.lArchivos.remove(pag) |
|---|
| 200 | self.PathArchivos.pop(num) |
|---|
| 201 | self.ActualizarPaginas() |
|---|
| 202 | self.OnCambiarPagina() |
|---|
| 203 | |
|---|
| 204 | self.pyragua.finicial.SetTitle("Pyragua") |
|---|
| 205 | return num |
|---|
| 206 | |
|---|
| 207 | def ActualizarPaginas(self): |
|---|
| 208 | """ Actualiza la numeracion de las ventanas al cerrarlas""" |
|---|
| 209 | NumeroPestanas= self.nArchivos.GetPageCount() |
|---|
| 210 | for i in range(1,int(NumeroPestanas)+1): |
|---|
| 211 | # separo el nombre del numero de la pagina |
|---|
| 212 | lista = self.nArchivos.GetPageText(i-1).split(' ')[1:len(self.nArchivos.GetPageText(i-1))] |
|---|
| 213 | cadena='' |
|---|
| 214 | # Uno la cadena |
|---|
| 215 | for j in lista: |
|---|
| 216 | if j == '*': |
|---|
| 217 | cadena=cadena+' '+j |
|---|
| 218 | break |
|---|
| 219 | cadena=cadena+j |
|---|
| 220 | # Coloco el nombre con la nueva numeracion en la ventana |
|---|
| 221 | self.nArchivos.SetPageText(i -1, str(i)+' '+cadena) |
|---|
| 222 | |
|---|
| 223 | def Ejecutar(self,evento=None): |
|---|
| 224 | #La ruta del ejecutable se puede obtener con sys.executable |
|---|
| 225 | if self.nArchivos.GetPageCount()>0:# Esto es para comprovar si hay pestañas abiertas |
|---|
| 226 | self.nArchivos.GetCurrentPage().Ejecutar(evento) |
|---|
| 227 | |
|---|
| 228 | def Guardar(self,evento): |
|---|
| 229 | """Guarda el archivo de la pestaña actual, retorna un booleano con que pasó y un |
|---|
| 230 | mensaje informativo""" |
|---|
| 231 | #La página actual seleccionada |
|---|
| 232 | pag=self.nArchivos.GetCurrentPage() |
|---|
| 233 | if self.nArchivos.GetPageCount()==0:# Esto es para comprovar si hay pestañas abiertas |
|---|
| 234 | return False,_("No ha abierto ningún Archivo") |
|---|
| 235 | else: |
|---|
| 236 | salida,info=pag.OnGuardar(evento) |
|---|
| 237 | if salida: |
|---|
| 238 | return True,_(u"Archivo %s guardado")%(path.basename(pag.nombre)) |
|---|
| 239 | else: |
|---|
| 240 | if info =="Cancelado": |
|---|
| 241 | return True,_(u"Cancelado %s\n%s"%(path.basename(pag.nombre)),"") |
|---|
| 242 | return False,_(u"Problemas guardando %s\n%s"%(path.basename(pag.nombre),info)) |
|---|
| 243 | |
|---|
| 244 | def GuardarComo(self,evento): |
|---|
| 245 | """Lo mismo que guardar pero cambia el nombre del archivo""" |
|---|
| 246 | #La página actual seleccionada |
|---|
| 247 | pag=self.nArchivos.GetCurrentPage() |
|---|
| 248 | if self.nArchivos.GetPageCount()==0:# Esto es para comprovar si hay pestañas abiertas |
|---|
| 249 | return False,_(u"No ha abierto ningun Arhivo") |
|---|
| 250 | else: |
|---|
| 251 | salida,info=pag.GuardarComo(evento) |
|---|
| 252 | if salida: |
|---|
| 253 | self.PathArchivos[self.nArchivos.GetSelection()]=pag.nombre |
|---|
| 254 | return True,_(u"Archivo %s guardado"%(path.basename(pag.nombre))) |
|---|
| 255 | else: |
|---|
| 256 | if info =="Cancelado": |
|---|
| 257 | return True,_(u"Cancelado %s\n%s"%(path.basename(pag.nombre),"")) |
|---|
| 258 | return False,_(u"Problemas guardando %s\n%s"%(path.basename(pag.nombre),info)) |
|---|
| 259 | |
|---|
| 260 | def Modificado(self, evento): |
|---|
| 261 | evento.Skip() |
|---|
| 262 | |
|---|
| 263 | def OnCambiarPagina(self,evento=None): |
|---|
| 264 | """Este evento se llamará cuando cambien de página, por el momento |
|---|
| 265 | se encarga de cambiar la codificacion en la barra de estado""" |
|---|
| 266 | if not evento : |
|---|
| 267 | pagina_actual=self.nArchivos.GetCurrentPage() |
|---|
| 268 | else: |
|---|
| 269 | pagina_actual=self.nArchivos.GetPage(evento.GetSelection()) |
|---|
| 270 | if pagina_actual: |
|---|
| 271 | self.padre.sBar.SetStatusText(pagina_actual.codificacion,1) |
|---|
| 272 | self.pyragua.finicial.pCodigo.aCodigo.CambiarArchivo(pagina_actual.nombre) |
|---|
| 273 | self.padre.MostrarEOL( self.nArchivos.GetSelection()) |
|---|
| 274 | self.pyragua.finicial.SetTitle("Pyragua " + path.basename(pagina_actual.nombre)) |
|---|
| 275 | if evento: evento.Skip() |
|---|
| 276 | |
|---|
| 277 | |
|---|
| 278 | def PopUpPestana ( self, evento ): |
|---|
| 279 | """Este método nos permite dar click derecho sobre la pestaña del notebook |
|---|
| 280 | para desplegar un menú""" |
|---|
| 281 | pos= evento.GetPosition() |
|---|
| 282 | self.IDcerrar = wx.NewId() |
|---|
| 283 | self.IndexPestana=self.nArchivos.HitTest(pos) |
|---|
| 284 | print self.IndexPestana |
|---|
| 285 | if self.IndexPestana: |
|---|
| 286 | # Hacemos un menú |
|---|
| 287 | menu = wx.Menu() |
|---|
| 288 | |
|---|
| 289 | # Mostramos íconos en las opciones |
|---|
| 290 | Size=(16,16) |
|---|
| 291 | item = wx.MenuItem(menu, self.IDcerrar,"Cerrar") |
|---|
| 292 | close_bmp = wx.ArtProvider.GetBitmap(wx.ART_DELETE, wx.ART_TOOLBAR, Size) |
|---|
| 293 | item.SetBitmap(close_bmp) |
|---|
| 294 | menu.AppendItem(item) |
|---|
| 295 | self.Bind(wx.EVT_MENU, self.PopUpClose, id=self.IDcerrar) |
|---|
| 296 | # Otros items |
|---|
| 297 | #_menu.Append(self.popupID2, "Two") |
|---|
| 298 | #_menu.Append(self.popupID3, "Three") |
|---|
| 299 | #_menu.Append(self.popupID4, "Four") |
|---|
| 300 | #_menu.Append(self.popupID5, "Five") |
|---|
| 301 | #_menu.Append(self.popupID6, "Six") |
|---|
| 302 | |
|---|
| 303 | # Para un submenú |
|---|
| 304 | #_sm = wx.Menu() |
|---|
| 305 | #_sm.Append(self.popupID8, "sub item 1") |
|---|
| 306 | #_sm.Append(self.popupID9, "sub item 1") |
|---|
| 307 | #_menu.AppendMenu(self.popupID7, "Test Submenu", sm) |
|---|
| 308 | |
|---|
| 309 | # Al dar click derecho sobre el popup se destruirá |
|---|
| 310 | self.PopupMenu(menu) |
|---|
| 311 | menu.Destroy() |
|---|
| 312 | |
|---|
| 313 | def PopUpClose ( self, evento): |
|---|
| 314 | pag=self.nArchivos.GetPage(self.IndexPestana[0]) |
|---|
| 315 | pag.stcEditor.EmptyUndoBuffer() |
|---|
| 316 | self.pyragua.cb.DelFile(self.nArchivos.GetPageText(self.IndexPestana[0])) |
|---|
| 317 | self.nArchivos.DeletePage(self.IndexPestana[0]) |
|---|
| 318 | self.lArchivos.remove(pag) |
|---|
| 319 | self.PathArchivos.pop(self.IndexPestana[0]) |
|---|
| 320 | self.ActualizarPaginas() |
|---|
| 321 | self.OnCambiarPagina() |
|---|