Zeichen unter Cursor im EditorGadget

Windowsspezifisches Forum , API ,..
Beiträge, die plattformübergreifend sind, gehören ins 'Allgemein'-Forum.
Benutzeravatar
Thorsten1867
Beiträge: 1360
Registriert: 04.02.2005 15:40
Computerausstattung: [Windows 10 x64] [PB V5.7x]
Wohnort: Kaufbeuren
Kontaktdaten:

Beitrag von Thorsten1867 »

Es ist vollbracht. Einfügen von Text (an der Cursorposition) in ein Editorgadget. Wenn nötig werden davor und/oder danach Leerzeichen eingefügt.

Code: Alles auswählen

Procedure.s Editor_GetCursorChar(Gadget_ID, opt.b=0)
  ; opt = 0 -> Character left from the cursor
  ; opt = 1 -> Character right from the cursor
  Protected ttr.TEXTRANGE
  Protected Buffer.s = Space(1)
  ttr\lpstrText = @Buffer
  SendMessage_(GadgetID(Gadget_ID),#EM_EXGETSEL,0,ttr\chrg)
  If opt
    ttr\chrg\cpMax + 1
  Else
    ttr\chrg\cpMin - 1
  EndIf
  SendMessage_(GadgetID(Gadget_ID),#EM_GETTEXTRANGE,0,ttr\chrg)
  ProcedureReturn Buffer
EndProcedure

Procedure InsertEG(Gadget_ID, text$)
  ; Insert text with spaces (if needed) in EditorGadget
  text$ = Trim(text$)
  If Editor_GetCursorChar(Gadget_ID, 0) <> " "
    text$ = " " + text$
  EndIf
  If Editor_GetCursorChar(Gadget_ID, 1) <> " "
    text$ + " "
  EndIf
  SendMessage_(GadgetID(Gadget_ID), #EM_REPLACESEL, 0, text$) 
EndProcedure
Zuletzt geändert von Thorsten1867 am 04.02.2007 01:03, insgesamt 2-mal geändert.
Download of PureBasic - Module
Download of PureBasic - Programmes

[Windows 11 x64] [PB V6]

Bild
Benutzeravatar
edel
Beiträge: 3667
Registriert: 28.07.2005 12:39
Computerausstattung: GameBoy
Kontaktdaten:

Beitrag von edel »

ttr\chrg\cpMin - 1 mit ttr\chrg\cpMax + 1 ersetzen.
Benutzeravatar
Thorsten1867
Beiträge: 1360
Registriert: 04.02.2005 15:40
Computerausstattung: [Windows 10 x64] [PB V5.7x]
Wohnort: Kaufbeuren
Kontaktdaten:

Beitrag von Thorsten1867 »

Danke! Habe es nach dem Trial & Error gerade selber herausgefunden. (siehe oben)
Download of PureBasic - Module
Download of PureBasic - Programmes

[Windows 11 x64] [PB V6]

Bild
Benutzeravatar
TomS
Beiträge: 1508
Registriert: 23.12.2005 12:41
Wohnort: München

Beitrag von TomS »

Hallo. Könnte man deinen Code so anpassen, dass er die ganze Zeile ermittelt oder den Ausdruck (getrennt von Leerzeichen oder Klammer)?

Ich möchte in einen eigenen editor eine Hilfefunktion, wie in der PB-IDE einbauen. F1 drücken und Hilfe zur Funktion bekommen, wo der Cursor steht.
Benutzeravatar
Thorsten1867
Beiträge: 1360
Registriert: 04.02.2005 15:40
Computerausstattung: [Windows 10 x64] [PB V5.7x]
Wohnort: Kaufbeuren
Kontaktdaten:

Beitrag von Thorsten1867 »

Vielleicht gibt es ja einen einfacheren Weg, aber ich würde es so machen:

Code: Alles auswählen

Procedure EGCursorLine(gadget) 
  SendMessage_(GadgetID(gadget),#EM_EXGETSEL,0,Range.CHARRANGE) 
  ProcedureReturn SendMessage_(REG,#EM_EXLINEFROMCHAR,0,Range\cpMin) 
EndProcedure

Procedure EGCursorCharPos(gadget) ; returns X-Pos of Cursor 
  REG = GadgetID(gadget) 
  SendMessage_(REG,#EM_EXGETSEL,0,Range.CHARRANGE) 
  ProcedureReturn (Range\cpMax-(SendMessage_(REG,#EM_LINEINDEX,SendMessage_(REG,#EM_EXLINEFROMCHAR,0,Range\cpMin),0))+1)
EndProcedure

Procedure.w GetWordStartPos(thText.s, thPos.w) ; Search position of first character from word
  If Mid(thText, thPos, 1) < "A" Or Mid(thText, thPos, 1) > "z" : ProcedureReturn thPos : EndIf
  Repeat
    thPos - 1
  Until Mid(thText, thPos, 1) = " " Or thPos <= 1
  If Mid(thText, thPos, 1) = " "
    ProcedureReturn thPos + 1
  Else
    ProcedureReturn 1
  EndIf
EndProcedure

Procedure.w GetWordEndPos(thText.s, thPos.w) ; Search position of last character from a word
  If Mid(thText, thPos, 1) < "A" Or Mid(thText, thPos, 1) > "z" : ProcedureReturn thPos : EndIf
  Repeat
    thPos + 1
  Until Asc(Mid(thText, thPos, 1)) < 65 Or Asc(Mid(thText, thPos, 1)) > 122 Or thPos >= Len(thText)
  If thPos <= Len(thText)
    ProcedureReturn thPos - 1 
  Else
    ProcedureReturn Len(thText)
  EndIf
EndProcedure

Procedure.s GetWord(text$, pos.w)
  wpos1.w = GetWordStartPos(text$, pos)
  ProcedureReturn Mid(text$, wpos1, GetWordEndPos(text$, pos) - wpos1 +1)
EndProcedure


hwnd = OpenWindow(0,0,0,300,300,"")

If CreateGadgetList(hwnd)
  hedit = EditorGadget(0,10,10,280,280)
EndIf
SetGadgetText(0, "Das ist ein Testtext.")
Repeat
  event = WaitWindowEvent()
  
  If event = #PB_Event_Gadget
    ; ......
  EndIf
  
  zeile$ = GetGadgetItemText(0,EGCursorLine(0),#Null)
  wort$ = GetWord(zeile$, EGCursorCharPos(0))
  SetWindowText_(WindowID(0), "Wort: " + wort$ + "  ("+Str(EGCursorCharPos(0))+")")
  
Until event = 16
Download of PureBasic - Module
Download of PureBasic - Programmes

[Windows 11 x64] [PB V6]

Bild
Benutzeravatar
TomS
Beiträge: 1508
Registriert: 23.12.2005 12:41
Wohnort: München

Beitrag von TomS »

Das is ja cool. Danke sehr!!
Benutzeravatar
edel
Beiträge: 3667
Registriert: 28.07.2005 12:39
Computerausstattung: GameBoy
Kontaktdaten:

Beitrag von edel »

Code: Alles auswählen

  Procedure.s getcurrblabla(gadget)
    Protected textr.TEXTRANGE 
    Protected *Bufptr.Character
    Protected *Gadget.long = IsGadget(gadget)
    
    Protected buffer.s
    Protected index.l
    Protected length.l 
    Protected start.l
    
    if *Gadget
      
      SendMessage_(*Gadget\l,#EM_EXGETSEL,0,textr)
      
      index     = SendMessage_(*Gadget\l,#EM_LINEINDEX,-1,0)
      length    = SendMessage_(*Gadget\l,#EM_LINELENGTH,textr\chrg\cpMax,0)
      
      if length
        
        currpos   = textr\chrg\cpMin
        buffer    = space(index + length)
        
        textr\chrg\cpMin = index
        textr\chrg\cpMax = index + length
        textr\lpstrText  = @buffer
        
        SendMessage_(*Gadget\l,#EM_GETTEXTRANGE,0,textr)
        
        *Bufptr = @buffer + (currpos - index - SizeOf(Character)) 
        
        Repeat 
          select *Bufptr\c
            case 'a' to 'z' , 'A' to 'Z' , '0' to '9' , '_'
              *Bufptr - SizeOf(Character) 
            default
              start = *Bufptr + SizeOf(Character)
              break
          EndSelect
        ForEver
        
        *Bufptr = @buffer + (currpos - index)
        
        Repeat 
          select *Bufptr\c
            case 'a' to 'z' , 'A' to 'Z' , '0' to '9' , '_'
              *Bufptr + SizeOf(Character)
            default
              *Bufptr\c = 0 
              break 
          EndSelect  
        ForEver
        
        buffer = peeks(start)
        
      endif 
      
    endif 
    
    ProcedureReturn buffer 
  EndProcedure 
Benutzeravatar
Thorsten1867
Beiträge: 1360
Registriert: 04.02.2005 15:40
Computerausstattung: [Windows 10 x64] [PB V5.7x]
Wohnort: Kaufbeuren
Kontaktdaten:

Beitrag von Thorsten1867 »

Heul! :cry:
Da programmiert man stundenlang, nur um festzustellen, dass es einen viel einfacheren Weg gibt. :wink:

Vielleicht sollte ich eine direkte Hotline für Editorgadgets zu dir legen. :mrgreen:

Nachdem du jetzt schon einige Dinge aus dem Ärmel gezaubert hast, probiere ich es doch mal mit einer Frage, die mich schon seit Ewigkeiten zur Verzweiflung treibt:

Projekt: Ich erzeuge aus dem Inhalt eines Editorgadget eine PDF-Ausgabe.
Problem: Die Textbreitenberechnung (automatischer Zeilenumbruch) ist äußerst ungenau (im Vergleich zu PDF), was manchmal zu unterschiedlichen Zeilenumbrüchen führt.
Frage 1: Gibt es eine Möglichkeit ein Editorgadget zu einem genaueren Zeilenumbruch zu bewegen.
Frage 2: Hast du Codeschnipsel, WinAPI-Befehle oder Ideen zum Erstellen eines eigenen Editorgadgets mit einer eigenen Breitenberechnung.

Für die Vorgehensweise der Berechnung gibt es einen C++ Code, aber das hilft mir nichts solange ich keine passendes Eingabefenster habe:

Code: Alles auswählen

/*----------------------------------------------------------------------
   GetCharDesignWidths:  Liefert die Zeichenbreiten der Schrift unskaliert
  ----------------------------------------------------------------------*/

UINT GetCharDesignWidths (HDC hdc, UINT uFirst, UINT uLast, int * piWidths)
{
     HFONT             hFont, hFontDesign ;
     LOGFONT           lf ;
     OUTLINETEXTMETRIC otm ;

     hFont = GetCurrentObject (hdc, OBJ_FONT) ;
     GetObject (hFont, sizeof (LOGFONT), &lf) ;

     // Outline-Textmetrik abfragen. Hier geht es ausschließlich um die Größe 
     // des vom Schriftdesigners verwendeten Koordinatensystems, der Geräte-
     // kontext spielt dabei keine Rolle
     otm.otmSize = sizeof (OUTLINETEXTMETRIC) ;
     GetOutlineTextMetrics (hdc, sizeof (OUTLINETEXTMETRIC), &otm) ;

     // Neue Schrift mit einer logischen Größe anlegen, die dem 
     // TrueType-intenen Koordinatensystem exakt entspricht
     lf.lfHeight = - (int) otm.otmEMSquare ;  // Zeichenhöhe, nicht Zeilenabstand!
     lf.lfWidth  = 0 ;
     hFontDesign = CreateFontIndirect (&lf) ;

     // Schrift in den Gerätekontext einsetzen und Zeichenbreiten ermitteln
     SaveDC (hdc) ;
     SetMapMode (hdc, MM_TEXT) ;
     SelectObject (hdc, hFontDesign) ;

     GetCharWidth (hdc, uFirst, uLast, piWidths) ;
     SelectObject (hdc, hFont) ;
     RestoreDC (hdc, -1) ;

     // Aufräumen
     DeleteObject (hFontDesign) ;

     return otm.otmEMSquare ;
}

/*---------------------------------------------------------------------------
   GetScaledWidths:  Liefert die Zeichenbreiten für die gewählte Schriftgröße
      als Gleitkommawerte
  ---------------------------------------------------------------------------*/

void GetScaledWidths (HDC hdc, double * pdWidths)
{
     double  dScale ;
     HFONT   hFont ;
     int     aiDesignWidths [LASTCHAR + 1] ;
     int     i ;
     LOGFONT lf ;
     UINT    uEMSquare ;

     // Outline-Zeichenbreiten abfragen
     uEMSquare = GetCharDesignWidths (hdc, 0, LASTCHAR, aiDesignWidths) ;

    // Abfrage der aktuell in den Kontext eingesetzten Schrift
     hFont = GetCurrentObject (hdc, OBJ_FONT) ;
     GetObject (hFont, sizeof (LOGFONT), &lf) ;

     // Zeichenbreiten entsprechend skalieren und als Gleitkomma-Array speichern
     dScale = (double) -lf.lfHeight / (double) uEMSquare ;

     for (i = 0 ; i <= LASTCHAR ; i++)
          pdWidths[i] = dScale * aiDesignWidths[i] ;
}

/*----------------------------------------------------------------
   GetTextExtentFloat:  Berechnet Textbreiten mit Gleitkommawerten
  ----------------------------------------------------------------*/

double GetTextExtentFloat (double * pdWidths, PTSTR psText, int iCount)
{
     double dWidth = 0 ;
     int    i ;

     for (i = 0 ; i < iCount ; i++)
          dWidth += pdWidths [psText[i]] ;

     return dWidth ;
}

/*--------------------------------------------------------------------------------------
   Justify mit Breitenberechnung über die Originaldaten der Schrift, ohne Rundungsfehler
  --------------------------------------------------------------------------------------*/

void Justify (HDC hdc, PTSTR pText, RECT * prc, int iAlign)
{
     double dWidth, adWidths[LASTCHAR + 1] ;
     int    xStart, yStart, cSpaceChars ;
     PTSTR  pBegin, pEnd ;
     SIZE   size ;

     // Array adWidths mit skalierten Gleitkomma-Zeichenbreiten besetzen
     GetScaledWidths (hdc, adWidths) ;

     yStart = prc->top ;
     do                            // für jede Textzeile
     {
          cSpaceChars = 0 ;        // bis jetzt 0 Leerzeichen

          while (*pText == ' ')    // führende Leerzeichen überspringen
               pText++ ; 
          pBegin = pText ;         // Zeiger auf den Zeilenanfang
          
          do                       // solange, bis die Zeile bearbeitet ist
          {
               pEnd = pText ;      // aktuelles Zeilenende (beim 1. Mal = Zeilenstart)

               // Suche nach dem ersten/nächsten Leerzeichen
               while (*pText != '\0' && *pText++ != ' ') ;

               if (*pText == '\0')
                    break ;

               // Leerzeichen gefunden. Wie viel Platz braucht die Zeile jetzt?
               cSpaceChars++ ;
               dWidth = GetTextExtentFloat (adWidths, pBegin, pText - pBegin - 1) ;
          }
          while (dWidth < (double) (prc->right - prc->left)) ;
          
          cSpaceChars-- ;               // dieses Leerzeichen nicht mitzählen
          
          while (*(pEnd - 1) == ' ')    // und weitere Leerzeichen am Zeilenende raus
          {
               pEnd-- ;
               cSpaceChars-- ;
          }

         // Textende erreicht oder keine Leerzeichen gefunden?
          if (*pText == '\0' || cSpaceChars <= 0)
               pEnd = pText ;

         // Platzbedarf erst jetzt über die Standardfunktion abfragen
          GetTextExtentPoint32(hdc, pBegin, pEnd - pBegin, &size) ;
          
          switch (iAlign)               // Ausrichtung == xStart
          {
          case IDM_ALIGN_LEFT:
               xStart = prc->left ;
               break ;

          case IDM_ALIGN_RIGHT:
               xStart = prc->right - size.cx ;
               break ;

          case IDM_ALIGN_CENTER:
               xStart = (prc->right + prc->left - size.cx) / 2 ;
               break ;
               
          case IDM_ALIGN_JUSTIFIED:
               if (*pText != '\0' && cSpaceChars > 0)
                    SetTextJustification (hdc, prc->right - prc->left - size.cx, cSpaceChars) ;
               xStart = prc->left ;
               break ;
          }
          // Ausgabe einer Zeile
          TextOut (hdc, xStart, yStart, pBegin, pEnd - pBegin) ;

          // Vorbereitungen für die nächste Zeile
          SetTextJustification (hdc, 0, 0) ;
          yStart += size.cy ;
          pText = pEnd ;
     }
     while (*pText && yStart < prc->bottom - size.cy) ;
}
Ich wäre für jeden Codeschnippsel und jede Anregung dankbar, bevor ich mich ans C++ lernen mache, um obigen Code nach PB zu übersetzen.

PS: Kompleter Code (C. Petzold): http://thorsten-hoeppner.de/download/Justify2.zip
Download of PureBasic - Module
Download of PureBasic - Programmes

[Windows 11 x64] [PB V6]

Bild
Benutzeravatar
edel
Beiträge: 3667
Registriert: 28.07.2005 12:39
Computerausstattung: GameBoy
Kontaktdaten:

Beitrag von edel »

Ohne Beispiel kann ich mir darunter jetzt nicht viel vorstellen.

>>aber das hilft mir nichts solange ich keine passendes Eingabefenster habe:

?

C brauchste dafuer nicht lernen , notfalls machste daraus eine Userlib.
Benutzeravatar
Thorsten1867
Beiträge: 1360
Registriert: 04.02.2005 15:40
Computerausstattung: [Windows 10 x64] [PB V5.7x]
Wohnort: Kaufbeuren
Kontaktdaten:

Beitrag von Thorsten1867 »

Dem Code liegt auch eine EXE bei, leider ist das Fenster nur RO und ich müsste Text eingeben, der dann korrekt umgebrochen wird, wie bei:

Code: Alles auswählen

eg = EditorGadget(#Gadget_Bem,17,190,598,41)
SendMessage_(eg, #EM_SETTARGETDEVICE, 0, 0)
SendMessage_(eg, #EM_SHOWSCROLLBAR, #SB_VERT, #False)
http://thorsten-hoeppner.de/download/Justify2.exe
Download of PureBasic - Module
Download of PureBasic - Programmes

[Windows 11 x64] [PB V6]

Bild
Antworten