viernes, 29 de junio de 2007

Probando : blockquote

Cita BEGIN

Huna sita


Y unas letras que se supone deben estar del mismo color que la cita (#C0C0C0 por el momento)

Cita END

Comenzando con OpenFile()

Por el momento ando un poco acelerado, hablando mentalmente, escucho metal de SOAD y me encuentro programando la función OpenFile(), lo de los cuadros de diálogo Abrir y Guardar quedó, por el momento resuelto: el siguiente código es una función que crea un cuadro de diálogo Abrir o Guardar según el parámetro que recibe (COMMDLGTYPE es una enumeración creada por mí), devuelve True si se abrió o guardó el archivo y cambia el nombre de las variables globales de mi programa que almacenan el nombre y la ruta del archivo. Ojo: sólo se encargan de crear los cuadros de diálogo, las funciones OpenFile(), SaveFile() y SaveFileAs() son las que estoy programando. Recordad que el código está aún imperfecto, pero la optimización irá al final, cuando todas las funciones y métodos del proyecto estén acabadas.

Function CommDlg(ByRef DlgType As COMMDLGTYPE) As Boolean
   
   Dim Ans As Boolean
   
   Dim Struct As OPENFILENAME
   With Struct
         'Internal properties
        .lStructSize = Len(Struct)
        .Flags = OFN_CREATEPROMPT Or OFN_PATHMUSTEXIST
        .hwndOwner = hwnd
        .lpstrInitialDir = CurDir$
        
         'Filters
        .lpstrFilter = Filters
        .lpstrCustomFilter = "All Files" & vbNullChar & "*.*"
        .nMaxCustFilter = 40
        .nFilterIndex = 0
        
         'File
        .lpstrFile = String(255, vbNullChar)
        .nMaxFile = 255
        .lpstrFileTitle = String(255, vbNullChar)
        .nMaxFileTitle = 255
   End With
   
   If DlgType = DlgOpen Then
      
      Struct.lpstrTitle = "ABRYR"
      Ans = GetOpenFileName(Struct)
      
   ElseIf DlgType = DlgSave Then
      
      Struct.lpstrFile = "HUARDAR"
      Ans = GetSaveFileName(Struct)
      
   End If
   
   If Ans Then
      FullFileName = VBA.Replace(Struct.lpstrFile, vbNullChar, vbNullString)
      FileName = VBA.Replace(Struct.lpstrFileTitle, vbNullChar, vbNullString)
   End If
   
   CommDlg = Ans
   
End Function


Pero, como dije antes, esto ya está concluido (momentáneamente concluido), cuando acabé eso hice otras cosas de menos importancia como definir más constantes.

El momento de comenzar a programar las funciones de los menús comenzó con el procedimiento NewFile(), el cuál estuvo muy sencillo de programar; paso siguiente es la función OpenFile() que, con ayuda del cuadro de diálogo Abrir, se encarga de cargar un archivo en el editor (WorkArea.Text)... pero nada de usar el método LoadFile() del RTF, más adelante hablaré sobre la filosofía y propósitos que sigue este proyecto.

El caso es: las funciones de manejo de archivos deben ser creadas por mí y no las ofrecidas por el control.

No es algo tan fácil como puede llegar a pensarse ya que no se trata de un TextStream extraído de un FileSystemObject, ya que eso sería demasiado lento (una deficiencia que pesará más cuanto mayor sea el tamaño del archivo), y consumiría el doble de espacio en memoria al llevarse a cabo la función ya que hablamos del String que se obtendrá del método ReadAll() de TextStream que se pasará a la propiedad .Text del RichTextBox.

Después de buscar pequeños instantes en MSDN me topé con la solución:

Public Const EM_STREAMIN = (WM_USER + 73)
Public Const EM_STREAMOUT = (WM_USER + 74)


Son mensajes para trabajar con los cuadros de texto enriquecido. Ambos de basan en una función interna que gestiona la información que transportan EditStreamCallBack. ¿Qué hace esta función callback? Pues, como su nombre lo dice, es una función que es llamada iterativamente cierta cantidad de veces siempre que el mensaje lo requiera.

DWORD CALLBACK EditStreamCallback(
  DWORD dwCookie, // application-defined value
  LPBYTE pbBuff,  // pointer to a buffer
  LONG cb,        // number of bytes to read or write
  LONG *pcb       // pointer to number of bytes transferred
);


Si usamos EM_STREAMIN la función reemplazará el contenido del RichTextBox con un 'cierto contenido' (ideal para cargar un archivo ¿no? :^>). Por el contrario, EM_STREAMOUT coloca el contenido del RTF a 'cierto lugar', lo que me resultará de gran utilidad a la hora de programar SaveFile().

¿Y por qué es más que crucial esta función? Porque usa un buffer para transmitir pequeñas cantidades de datos. El 'cierto contenido' viaja a través de una variable que especificamos en el CallBack hacia el 'cierto lugar' que puede ser un archivo (para salida de datos con EM_STREAMOUT) o nuestro RTF WorkArea si se usa el mensaje EM_STREAMIN.

Cuando se manda un mensaje EM_STREAMIN/OUT (el valor de wParam debe ser un tipo de datos EDITSTREAM), el CallBack especificado en EDITSTREAM.pfnCallback se llama mientras el buffer no haya terminado de traspasar el contenido del RichTextBox, o mientras no se genere algún error... cosas como esas.

Así es como veo solucionado el problema de memoria y supongo también que el de velocidad.

El problema va a ser programar esa función CallBack; no la puedo implementar a modo de API porque al mandar el mensaje simplemente se empieza a llamar iterativamente a la función, y se supone que es ahí donde tengo que especificar de dónde a dónde va a transmitir información el Buffer.

En eso estoy... o a eso voy más bien puesto que ya tengo el cuerpo vacío de la función y voy al código,,,

lunes, 25 de junio de 2007

Rekapitulerende i prosjekten NekroEditor

Estamos de vuelta otra vez en las andadas de hace dos diciembres: dos años atrás comencé este proyecto.

Recapitulemos algo sobre cómo comenzó esto: fue en el año 2005 cuando se me ocurrió esto, aunque la fecha exacta luce ensombrecida. Hay un post: NekroEditor con fecha ''martes 27 de diciembre de 2005'' donde escribo:

Hoy me he propuesto comenzar bien un proyecto que quería hacer desde hace meses


Creo, teorizo, que con "hace meses" me refería al pasado abril... sólo es una corazonada.

La fecha exacta no es del todo clara ya que, pese a la fecha en el que publico que me he decidido, en las propiedades del modMain.bas aparece: Domingo, 25 de Diciembre de 2005, 04:03:40 p.m. El frmMain.frm y NekroEditor.vbp que uso ahora, que serían fuentes más fiables (más el último) fueron recreados ayer por unas movidas que les hice a los archivos, perdiéndose los originales.

Pero, según recuerdo, fue por abril o marzo cuando comencé a hacer bocetos e intentos en VB tramando el suceso, pero fue hasta aquel diciembre de masoquistas desvelos cuando arranqué muy de lleno y... casi todo lo que hice está narrado en las otras entradas.

<acheere diagonal>



¿Y qué hay de ahora?

Fue ayer o antier cuando proseguí el camino, después de haber tomado un firme entrenamiento y adquirido experiencia en C y Java.

Me embarqué en varias cosas que había dejado pendientes, junto con el proyecto, por ejemplo, necesitaba demostrarme que podía tener control con funciones que cambien el formato del texto, y me sorprende que ahora que estoy de vuelta haya resuelto ese problema en cuestión de minutos y casi sin dificultades (lo único que hice fue programar la función, lo demás ya lo tenía):

Type CHARFORMAT2
   cbSize As Long
   dwMask As Long
   dwEffects As Long
   yHeight As Long
   yOffset As Long
   crTextColor As Long
   bCharset As Byte
   bPitchAndFamily As Byte
   szFaceName(65) As Byte
   wWeight As Integer
   sSpacing As Integer
   crBackColor As Long
   lcid As Long
   dwReserved As Long
   sStyle As Integer
   wKerning As Integer
   bUnderlineType As Byte
   bAnimation As Byte
   bRevAuthor As Byte
   bReserved1 As Byte
End Type

Enum Selection
   SCF_SELECTION = &H1
   SCF_WORD = &H2 Or &H1 '(SCF_WORD | SCF_SELECTION)
   SCF_ALL = &H4
   SCF_USEUIRULES = &H8
   SCF_DEFAULT = &H0
End Enum


Sub SetBold(ByVal Sel As Selection)

   Dim Format As CHARFORMAT2
   With Format
      .cbSize = Len(Format)
      .dwMask = CFM_BOLD
      .dwEffects = CFE_BOLD
   End With

   Call SendMessage(hWorkArea, EM_SETCHARFORMAT, Sel, Format)

End Sub


Es poco, pero para mí, que no sabía mucho en aquel entonces me resultaba muy mareador el pensar que debía aprender a controlar todas las propiedades de ese tipo de datos tan grande.

Lo que vino después fue hacer los prototipos de las funciones que iba a tener mi programa: las generales que son las que interactúan con el usuario (OpenFile, SaveFile, Find, Replace) y las internas que asisten el programa en sus labores durante la edición tales como SetBold, SetColor, SetFont, LoadFile y demás. Repito y aclaro, únicamente fue cuestión de crear los prototipos, dos conjuntos de Sub [Nombre]() ' End Sub \ que luego programaré; más que nada fue para asignárselas a los Select Case LoWord(wParam), que es lo que me dice qué opción del menú se presionó (tuve también que investigar eso y programar las funciones LoWord() y HiWord().

En el intermedio que hubo al hacer cada grupo de prototipos hice unos cambios en la organización de archivos: pasaron de estar todos los formularios, módulos, imágenes y demás a reorganizarse en las carpetas /Documentación, /Forms, /Main, /Modules y /Resources (la carpeta de documentación la cambiaré después a una llamada /Docs o algo así. Me lié con ello, pues no solo era cuestión de mover archivos sino también las imágenes de otros dos formularios (de búsqueda y reemplazo) y las de la barra de herramientas, en fin, fue un rollo que, al parecer, recreó varios archivos.

Y lo último más importante que hay que mencionar es lo que me encuentro tratando de resolver ahora: crear un cuadro de diálogo con APIs.

Habrá que usar GetOpenFile y GetSaveFile pero el problema no son ellas, sino el único parámetro que llevan: un tipo de dato OPENFILENAME

Public Type OPENFILENAME
      lStructSize As Long
      hwndOwner As Long
      hInstance As Long
      lpstrFilter As String
      lpstrCustomFilter As String
      nMaxCustFilter As Long
      nFilterIndex As Long
      lpstrFile As String
      nMaxFile As Long
      lpstrFileTitle As String
      nMaxFileTitle As Long
      lpstrInitialDir As String
      lpstrTitle As String
      Flags As Long
      nFileOffset As Integer
      nFileExtension As Integer
      lpstrDefExt As String
      lCustData As Long
      lpfnHook As Long
      lpTemplateName As String
End Type


que al principio me intimidó pero al final descubrí que era como el CHARFORMAT2: sólo ocupas los elementos que necesites, los demás quedan descartados.

Aunque no es del todo sencillo ya que hay que someterse a los caprichos de la API. El procedimiento que estoy programando es el segundo prototipo en desarrollo que maquilo (el primero fue SetBold), aún imperfecto y no del todo acabo funciona bien, es el bloque DlgOpen que se encarga de crear un cuadro de diálogo para abrir un archivo y es el que me ayudará con la función OpenFile. Como vi que crear cuadros de diálogo no era algo sencillo y de poco código estoy asignando las funciones de cuadros de diálogo en un recién creado modDialogs.bas

Busqué algo de ayuda en Google y, con un par de buenos ratos resumí el primer paso que es conseguir abrir el cuadro de diálogo desde Archivo / Abrir:

Function DlgOpen() As Boolean

   Dim SNull As String * 1: SNull = Chr$(0) 'Single Null String
   Dim DNull As String * 2: DNull = Chr$(0) & Chr$(0) 'Double Null String

   Dim Flags&
   Flags = OFN_CREATEPROMPT Or OFN_ENABLESIZING Or OFN_EXPLORER Or OFN_PATHMUSTEXIST

   Dim FilterIndex As Byte
   Dim Filters As String
   Filters = "Text Files (*.txt)" & SNull & "*.txt" & SNull + _
            "All Files (*.*)" & SNull & "*.*" & DNull

   Dim Trash As Integer

   Dim Struct As OPENFILENAME
   With Struct
          'Internal properties
         .lStructSize = Len(Struct)
         .Flags = Flags
         .nFileOffset = Trash
         .nFileExtension = Trash

          'Simply
         .hwndOwner = hwnd
         .hInstance = App.hInstance

          'Filters
         .lpstrFilter = Filters
         .lpstrCustomFilter = 2
         .nMaxCustFilter = Len(Filters)
         .nFilterIndex = FilterIndex

          'File
         .lpstrFile = Space(254)
         .nMaxFile = 255
         .lpstrFileTitle = Space(254)
         .nMaxFileTitle = 255

          'Dialog properties
         .lpstrInitialDir = CurDir$
         .lpstrTitle = "TITULER"
         .lpstrDefExt = vbNull
   End With

   DlgOpen = GetOpenFileName(Struct)

End Function


Seguiré trabajando en eso pero como veo que este proyecto tendrá cierta trascendencia después de haber sido inspirado entre otras cosas por el KWrite decidí añadirle su categoría en el blog, además de la de Visual Basic: NekroEditor.

miércoles, 6 de junio de 2007

La parábola del pato

Dos ranchos vecinos tenían dos mismos problemas con sus gallinas: primero, eran muchas; segundo, eran muy nerviosas y cada que escuchaban el mínimo estrépito comenzaban a cacarear y correr estúpidamente en todas las direcciones.

Cierto día, en cada rancho, nació un pollo que había sido fervientemente empollado por su madre durante 10 meses. A los 15 minutos después de que el pollito nació los granjeros daban una pasada al gallinero para ver cómo seguían las cosas, pero al abrir la puerta las gallinas comenzaron a revolotear por toda el área y el pollito, nervioso, hizo lo mismo para huir, pero apenas y se apartó de su madre poniéndose a la intemperie, fue pisoteado por todas las gallinas que pasaban. Los granjeros acudieron rápidamente a sostener al pollito y retenerlo entre sus brazos hasta que el escándalo se calmó.

Finalmente, cuando las gallinas se acostumbraron a la presencia de los granjeros, éstos se dispusieron a ver en qué estado se encontraba el pollito; fue terrible su sorpresa al verlo agonizante, lleno de espasmos y al borde de la muerte.

- ¡Esto es suficiente! -dijo uno de ellos - La única que puede darle lo que necesita es su madre, nosotros no podríamos estarlo calentando todo el tiempo y dándole alimentos tan suaves como lo necesite. Y si lo dejamos aquí es seguro que mañana mismo morirá por los nervios de las gallinas. No podemos esperar a que crezca, además, es tan pequeño y tan débil que si logra sobrevivir será un milagro. Creo que lo mejor es acabar de una vez con su sufrimiento y matarlo, ya gallinas y gallos tenemos suficientes.

Y asesinaron al pollito.

En ese mismo instante, en el rancho vecino, había ocurrido exactamente lo mismo, pero el granjero que sostenía al pollito, al terminar el escándalo, resolvió:

- Este pollo es muy pequeño, lleva apenas 15 minutos de nacido y será muy difícil que sobreviva a los nervios de estas gallinas; sugiero que nos turnemos y lo cuidemos, así nos aseguraremos de que no le pase nada, después de todo somos muchos y el trabajo no será muy pesado. Estoy seguro que con el tiempo crecerá y se convertirá en un orgulloso gallo que cantará triunfante por las mañanas.

Y así fue.




Ahora, ¿por qué "la parábola del pato" si es un pollito?... ¡pues porque me gusta más cómo se oye 'pato' pero también me agrada más cómo se narra siendo el protagonista un pollito!, es todo.

Pero ese no es el punto. Quiero hablar de un caso que ha ocurrido en la Wikipedia: en nuestra Wikipedia, el artículo de Christopher Von Uckermann creado hace casi un año, está entre los artículos propuestos para borrar y actualmente se encuentra sometido a votación. Voy a la página de consultas y me encuentro con que 5 están en contra y leo en los comentarios:

"es preferible borrarlo que mantener un Esbozo de mala calidad y poco valor enciclopédico."

"Las estrellitas con quince minutos de fama carecen de relevancia enciclopédica"

Intento categorizarlo y voy a la Wikipedia en inglés directo a sorprenderme: un tremendo texto sobre él, con una buena sección de su biografía, la discografía de RBD, el artículo perfectamente categorizado y enlazado con los demás interwikis, de verdad me encontré con un trabajo de lo mejor.

Fue entonces cuando se me vino a la mente: es una estrellita de 15 minutos de fama, pero mientras que en la Wikipedia ES lo apedrearon y lo quieren condenar a muerte por pequeño, en la Wikipedia EN no perdieron tiempo en eso y se dedicaron a hacerlo crecer hasta convertirlo en el gran ensayo que es ahora. Es por ello que nuestra Wikipedia está así y la EN está ASÍ.

Perfectamente dejé mi respuesta en los comentarios después de votar en contra del borrado:

Con esa actitud la Wikipedia no florecerá, es la tendencia de impedir que una rana salga de la cubeta. Está bien que sea una "estrellita del momento", pero así nacieron los grandes actores, y crecieron los miniesbozos hasta convertirse en artículos destacados, incluso Google sostiene que "de las cosas más pequeñas se hacen las más grandes". En la Wikipedia en inglés (siempre próspera) aquel artículo de Uckermann que comenzó como un esbozo, ahora está fuertemente documentado y categorizado, pero logró ese éxito con la ayuda de varios usuarios, no con la mediocridad de quienes quieren hundir lo que ven pequeño. Finalmente, no es del todo un actor fugaz, pues ya había trabajado en novelas anteriormente, inclusive ahora que leo las novelas en que trabajó, me doy cuenta que es el mismo adolescente que veía en Amigos X Siempre (también veía El Diario de Daniela pero fue hace tanto tiempo que ni me acuerdo). En lugar de perder el tiempo sumergiendo en el lodo este artículo, escriban ya no una sección, sino un párrafo que lean sobre él. Si se somete al vandalismo, para eso estamos los Wikipedistas: cazadores de vándalos. --NekroByte 04:26 7 jun 2007 (CEST)


Y cuando quieran pueden ver cómo quedaron las cosas en nuestra Wikipedia. Estoy seguro de que si lo borran, en el futuro alguien lo volverá a crear, pero lo que temo es que vuelvan a asesinar al pobrecito.

Hilsener.




Relacionado: Se buscan Wikipedistas