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 »

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
Benutzeravatar
edel
Beiträge: 3667
Registriert: 28.07.2005 12:39
Computerausstattung: GameBoy
Kontaktdaten:

Beitrag von edel »

Ahh, denke das ich es verstanden habe.

Ob das so mit dem EditorGadget geht, weiss ich nicht. Mir faellt da nur
EM_SETWORDBREAKPROC ein , ob und wie dir das hilft, keine Ahnung,
habe damit noch nie etwas gemacht.

Falls du selber etwas machen moechtest schau dir auf MSDN mal
die Seite Using Carets an.
Andreas
ToolbarKönig
Beiträge: 142
Registriert: 08.09.2004 08:33

Beitrag von Andreas »

TomS hat geschrieben:Das is ja cool. Danke sehr!!
Oder so etwas vielleicht ?

Code: Alles auswählen

Procedure GetCurrentWord(Gadget)
  Protected result$,selstart.l,start.l,len.l
  SendMessage_(GadgetID(Gadget),#EM_EXGETSEL,0,ch.CHARRANGE)
  start = SendMessage_(GadgetID(Gadget),#EM_FINDWORDBREAK,0,CH\cpMin)
  len = SendMessage_(GadgetID(Gadget),#EM_FINDWORDBREAK,#WB_RIGHTBREAK,CH\cpMin) - start
  *buffer = AllocateMemory(len)
  tr.TEXTRANGE
  tr\chrg\cpmin = start
  tr\chrg\cpmax = start+len
  tr\lpstrtext = *Buffer
  SendMessage_(GadgetID(Gadget),#EM_GETTEXTRANGE,0,tr)
  Result$ = PeekS(*Buffer)
  FreeMemory(*Buffer)
  MessageRequester(Result$,"Word under Cursor",0)
EndProcedure

If OpenWindow(0, 10, 10, 400, 300, "PureBasic Window", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget)
  If CreateGadgetList(WindowID(0))
    ButtonGadget(1, 10,200,120,24,"GetCurrentWord")
    EditorGadget(0, 8, 8, 306, 133)
    SetGadgetText(0,"Das ist Text zum testen ! Setz den Cursor über ein Wort.")
  EndIf
  Repeat
    EventID = WaitWindowEvent()
    Select EventID
    Case #PB_Event_CloseWindow
      Quit = 1
    Case #PB_Event_Gadget
      Select EventGadget()
      Case 1
        GetCurrentWord(0)
      EndSelect
    EndSelect
  Until Quit = 1
EndIf
End

Gruss Andreas
Benutzeravatar
ts-soft
Beiträge: 22292
Registriert: 08.09.2004 00:57
Computerausstattung: Mainboard: MSI 970A-G43
CPU: AMD FX-6300 Six-Core Processor
GraKa: GeForce GTX 750 Ti, 2 GB
Memory: 16 GB DDR3-1600 - Dual Channel
Wohnort: Berlin

Beitrag von ts-soft »

<offtopic>
@Andreas
Wie sieht es aus mit AvEditor für PB4?
</offtopic>
PureBasic 5.73 LTS | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Nutella hat nur sehr wenig Vitamine. Deswegen muss man davon relativ viel essen.
Bild
Andreas
ToolbarKönig
Beiträge: 142
Registriert: 08.09.2004 08:33

Beitrag von Andreas »

ts-soft hat geschrieben:<offtopic>
@Andreas
Wie sieht es aus mit AvEditor für PB4?
</offtopic>
Noch nicht ganz fertig. Im Moment finde ich nicht die Zeit dazu, wird aber werden.
Gruss Andreas
Benutzeravatar
ts-soft
Beiträge: 22292
Registriert: 08.09.2004 00:57
Computerausstattung: Mainboard: MSI 970A-G43
CPU: AMD FX-6300 Six-Core Processor
GraKa: GeForce GTX 750 Ti, 2 GB
Memory: 16 GB DDR3-1600 - Dual Channel
Wohnort: Berlin

Beitrag von ts-soft »

Andreas hat geschrieben:Noch nicht ganz fertig. Im Moment finde ich nicht die Zeit dazu, wird aber werden.
Hört sich gut an :allright:
PureBasic 5.73 LTS | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Nutella hat nur sehr wenig Vitamine. Deswegen muss man davon relativ viel essen.
Bild
Benutzeravatar
TomS
Beiträge: 1508
Registriert: 23.12.2005 12:41
Wohnort: München

Beitrag von TomS »

Andreas hat geschrieben:
TomS hat geschrieben:Das is ja cool. Danke sehr!!
Oder so etwas vielleicht ?

Code: Alles auswählen

Procedure GetCurrentWord(Gadget)
  Protected result$,selstart.l,start.l,len.l
  SendMessage_(GadgetID(Gadget),#EM_EXGETSEL,0,ch.CHARRANGE)
  start = SendMessage_(GadgetID(Gadget),#EM_FINDWORDBREAK,0,CH\cpMin)
  len = SendMessage_(GadgetID(Gadget),#EM_FINDWORDBREAK,#WB_RIGHTBREAK,CH\cpMin) - start
  *buffer = AllocateMemory(len)
  tr.TEXTRANGE
  tr\chrg\cpmin = start
  tr\chrg\cpmax = start+len
  tr\lpstrtext = *Buffer
  SendMessage_(GadgetID(Gadget),#EM_GETTEXTRANGE,0,tr)
  Result$ = PeekS(*Buffer)
  FreeMemory(*Buffer)
  MessageRequester(Result$,"Word under Cursor",0)
EndProcedure

If OpenWindow(0, 10, 10, 400, 300, "PureBasic Window", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget)
  If CreateGadgetList(WindowID(0))
    ButtonGadget(1, 10,200,120,24,"GetCurrentWord")
    EditorGadget(0, 8, 8, 306, 133)
    SetGadgetText(0,"Das ist Text zum testen ! Setz den Cursor über ein Wort.")
  EndIf
  Repeat
    EventID = WaitWindowEvent()
    Select EventID
    Case #PB_Event_CloseWindow
      Quit = 1
    Case #PB_Event_Gadget
      Select EventGadget()
      Case 1
        GetCurrentWord(0)
      EndSelect
    EndSelect
  Until Quit = 1
EndIf
End

Man man man. Ihr pflastert mich hier mit Codes zu, ich weiß gar nicht welchen ich verwenden soll. :oops: Danke euch allen. :allright:
Antworten