Move to a position in an EditGadget

Just starting out? Need help? Post your questions and find answers here.
User avatar
Shardik
Addict
Addict
Posts: 1991
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Re: Move to a position in an EditGadget

Post by Shardik »

marcoagpinto wrote:Now there is only the Linux version missing :p
A search in this forum would have shown you already a cross-platform solution for Linux, Windows and MacOS X (supporting even both Cocoa and Carbon framework) that demonstrates how to select a specific line in the EditorGadget. I have customized that example even further so that a double click in the upper ListIconGadget (or selecting a line and pressing the Find button) will search the text in the lower EditorGadget, select that line and scroll it into view.

I have tested the example below successfully with these operating systems and both PB 5.11 and PB 5.20 Beta 15:
- andLinux/Kubuntu 9.04 x86
- Ubuntu 12.04 x64 with Unity and KDE
- Windows XP SP3 x86
- Windows 7 SP1 x64
- MacOS X 10.6.8 (Snow Leopard) x86 and x64
- MacOS X 10.8.4 (Mountain Lion) x86 and x64

Code: Select all

EnableExplicit

CompilerSelect #PB_Compiler_OS
  CompilerCase #PB_OS_MacOS
    CompilerIf #PB_Compiler_Version <= 461 Or (#PB_Compiler_Version >= 500 And Subsystem("Carbon"))
      ImportC ""
        GetControlProperty(ControlRef.L, PropertyCreator.L, PropertyTag.L, BufferSize.L, *ActualSize, *PropertyBuffer)
        TXNSetSelection(TXNObject.L, StartOffset.L, EndOffset.L)
        TXNShowSelection(TXNObject.L, ShowEnd.L)
      EndImport
    CompilerEndIf
  CompilerCase #PB_OS_Linux
    ImportC ""
      gtk_text_view_scroll_to_iter(*TextView.GtkTextView, *Iter.GtkTextIter, WithinMargin.D, UseAlign.I, xAlign.D, yAlign.D)
    EndImport
CompilerEndSelect

Enumeration 
  #ListIcon
  #Button
  #Editor
EndEnumeration

Procedure SelectEditorLine(EditorID.I, LineNumber.I)
  CompilerSelect #PB_Compiler_OS
    CompilerCase #PB_OS_Linux
      Protected EndOfLine.GtkTextIter
      Protected StartOfLine.GtkTextIter
      Protected *TextBuffer.GtkTextBuffer
      
      *TextBuffer = gtk_text_view_get_buffer_(GadgetID(EditorID))
      gtk_text_buffer_get_iter_at_line_(*TextBuffer, @StartOfLine, LineNumber)
      gtk_text_view_scroll_to_iter(GadgetID(EditorID), StartOfLine, 0.0, #False, 0.0, 0.0)
      EndOfLine = StartOfLine
      gtk_text_iter_forward_to_line_end_(EndOfLine)
      gtk_text_buffer_select_range_(*TextBuffer, StartOfLine, EndOfLine)
    CompilerCase #PB_OS_MacOS
      CompilerIf #PB_Compiler_Version <= 461 Or (#PB_Compiler_Version >= 500 And Subsystem("Carbon"))
        Protected EndOfLine.L
        Protected i.L
        Protected StartOfLine.L
        Protected TXNObject.L
        
        If LineNumber > 0
          For i = 0 To LineNumber - 1
            StartOfLine + Len(GetGadgetItemText(EditorID, i)) + 1
          Next i
        EndIf
        
        If GetControlProperty(GadgetID(EditorID), $50555245, $54584F42, 4, 0, @TXNObject) = 0
          EndOfLine = StartOfLine + Len(GetGadgetItemText(EditorID, LineNumber))
          TXNSetSelection(TXNObject, StartOfLine, EndOfLine)
          TXNShowSelection(TXNObject, #True)
        EndIf
      CompilerElse
        Protected i.I
        Protected Range.NSRange
        
        If LineNumber > 0
          For i = 0 To LineNumber - 1
            Range\location + Len(GetGadgetItemText(EditorID, i)) + 1
          Next i
        EndIf
        
        Range\length = Len(GetGadgetItemText(EditorID, LineNumber))
        CocoaMessage(0, GadgetID(EditorID), "setSelectedRange:@", @Range)
        CocoaMessage(0, GadgetID(EditorID), "scrollRangeToVisible:@", @Range)
      CompilerEndIf
    CompilerCase #PB_OS_Windows
      Protected LineStart.I
      
      LineStart = SendMessage_(GadgetID(#Editor), #EM_LINEINDEX, LineNumber, 0)
      SendMessage_(GadgetID(#Editor), #EM_SETSEL, LineStart, LineStart + Len(GetGadgetItemText(#Editor, LineNumber)))
  CompilerEndSelect
EndProcedure

Procedure FindText(EditorID, Text.S)
  Protected LinesTotal.I
  Protected LineNumber.I
  Protected LineStart.I
     
  LinesTotal = CountGadgetItems(#ListIcon)

  If LinesTotal > 0
    For LineNumber = 0 To LinesTotal - 1
      If FindString(GetGadgetItemText(#Editor, LineNumber), Text) > 0
        SelectEditorLine(EditorID, LineNumber)
        Break
      EndIf
    Next LineNumber
  EndIf
EndProcedure

Define i.I

OpenWindow(0, 100, 100, 261, 230, "Find clicked text in Editor")
ListIconGadget(#ListIcon, 10, 10, WindowWidth(0) - 20, 90, "Column 1", 110, #PB_ListIcon_FullRowSelect)
AddGadgetColumn(#ListIcon, 1, "Column 2", GadgetWidth(#ListIcon) - GetGadgetItemAttribute(#ListIcon, 0, #PB_ListIcon_ColumnWidth, 0) - 24)
ButtonGadget(#Button, 35, GadgetY(#ListIcon) + GadgetHeight(#ListIcon) + 10, 190, 25, "Find selected text in Editor")
EditorGadget(#Editor, 10, GadgetY(#Button) + GadgetHeight(#Button) + 10, WindowWidth(0) - 20, WindowHeight(0) - GadgetY(#Button) - GadgetHeight(#Button) - 20, #PB_Editor_ReadOnly)

For i= 1 To 20
  AddGadgetItem(#ListIcon, -1, "Row " + Str(i))
Next

For i = 1 To 20
  AddGadgetItem(#Editor, -1, "Row " + Str(i) + " in EditorGadget")
Next i

Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      Break
    Case #PB_Event_Gadget
      Select EventGadget()
        Case #ListIcon
          If EventType() = #PB_EventType_LeftDoubleClick
            FindText(#Editor, StringField(GetGadgetItemText(#ListIcon, GetGadgetState(#ListIcon)), 1, ","))
          EndIf
        Case #Button
          If GetGadgetState(#ListIcon) >= 0
            FindText(#Editor, StringField(GetGadgetItemText(#ListIcon, GetGadgetState(#ListIcon)), 1, ","))
          EndIf
          
          SetActiveGadget(#ListIcon)
      EndSelect
  EndSelect
ForEver
Update: I had to improve the Mac part in SelectEditorLine() because it worked only in this example but not on a general basis. Thank you, Danilo, for pinpointing this error!
Last edited by Shardik on Sat Aug 31, 2013 10:01 am, edited 1 time in total.
User avatar
marcoagpinto
Addict
Addict
Posts: 947
Joined: Sun Mar 10, 2013 3:01 pm
Location: Portugal
Contact:

Re: Move to a position in an EditGadget

Post by marcoagpinto »

Thank you guys for all the help :)
User avatar
Shardik
Addict
Addict
Posts: 1991
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Re: Move to a position in an EditGadget

Post by Shardik »

For those interested in cross-platform code examples utilizing specific API functions for Linux, MacOS X and Windows, I have collected links in this posting:
http://www.purebasic.fr/english/viewtop ... 3&start=11
User avatar
USCode
Addict
Addict
Posts: 912
Joined: Wed Mar 24, 2004 11:04 pm
Location: Seattle, USA

Re: Move to a position in an EditGadget

Post by USCode »

Wouldn't it be better in the long-term to look into the ScintillaGadget? http://www.purebasic.com/documentation/ ... adget.html
It will give your application much more flexibility and you won't need to resort to researching API calls on different platforms all the time to make it cross-platform ...
User avatar
Danilo
Addict
Addict
Posts: 3037
Joined: Sat Apr 26, 2003 8:26 am
Location: Planet Earth

Re: Move to a position in an EditGadget

Post by Danilo »

Shardik wrote:
marcoagpinto wrote:Now there is only the Linux version missing :p
A search in this forum would have shown you already a cross-platform solution for Linux, Windows and MacOS X (supporting even both Cocoa and Carbon framework) that demonstrates how to select a specific line in the EditorGadget. I have customized that example even further so that a double click in the upper ListIconGadget (or selecting a line and pressing the Find button) will search the text in the lower EditorGadget, select that line and scroll it into view.

I have tested the example below successfully with these operating systems and both PB 5.11 and PB 5.20 Beta 15:
- andLinux/Kubuntu 9.04 x86
- Ubuntu 12.04 x64 with Unity and KDE
- Windows XP SP3 x86
- Windows 7 SP1 x64
- MacOS X 10.6.8 (Snow Leopard) x86 and x64
- MacOS X 10.8.4 (Mountain Lion) x86 and x64
marcoagpinto was just looking for code to set the cursor position, not selecting a line. It's different, and maybe
he didn't know how to do it himself, so he asked.

Your procedure SelectEditorLine(EditorID.I, LineNumber.I) has some problems on MacOS X. It works in your special example,
but it is not useable for general purposes.
I would think this procedure selects the text of a specific EditorGadget line, but it does not work correctly with your
implementation:

Code: Select all

Range\location = FindString(GetGadgetText(EditorID), GetGadgetItemText(EditorID, LineNumber)) - 1
It is searching for the text at LineNumber, but if this text is already found before, the wrong part is selected.

Small example using your procedure:

Code: Select all

EnableExplicit

CompilerSelect #PB_Compiler_OS
  CompilerCase #PB_OS_MacOS
    CompilerIf #PB_Compiler_Version <= 461 Or (#PB_Compiler_Version >= 500 And Subsystem("Carbon"))
      ImportC ""
        GetControlProperty(ControlRef.L, PropertyCreator.L, PropertyTag.L, BufferSize.L, *ActualSize, *PropertyBuffer)
        TXNSetSelection(TXNObject.L, StartOffset.L, EndOffset.L)
        TXNShowSelection(TXNObject.L, ShowEnd.L)
      EndImport
    CompilerEndIf
  CompilerCase #PB_OS_Linux
    ImportC ""
      gtk_text_view_scroll_to_iter(*TextView.GtkTextView, *Iter.GtkTextIter, WithinMargin.D, UseAlign.I, xAlign.D, yAlign.D)
    EndImport
CompilerEndSelect

Enumeration 
  #Editor
EndEnumeration

Procedure SelectEditorLine(EditorID.I, LineNumber.I)
  CompilerSelect #PB_Compiler_OS
    CompilerCase #PB_OS_Linux
      Protected EndOfLine.GtkTextIter
      Protected StartOfLine.GtkTextIter
      Protected *TextBuffer.GtkTextBuffer
     
      *TextBuffer = gtk_text_view_get_buffer_(GadgetID(EditorID))
      gtk_text_buffer_get_iter_at_line_(*TextBuffer, @StartOfLine, LineNumber)
      gtk_text_view_scroll_to_iter(GadgetID(EditorID), StartOfLine, 0.0, #False, 0.0, 0.0)
      EndOfLine = StartOfLine
      gtk_text_iter_forward_to_line_end_(EndOfLine)
      gtk_text_buffer_select_range_(*TextBuffer, StartOfLine, EndOfLine)
    CompilerCase #PB_OS_MacOS
      CompilerIf #PB_Compiler_Version <= 461 Or (#PB_Compiler_Version >= 500 And Subsystem("Carbon"))
        Protected EndOfLine.L
        Protected StartOfLine.L
        Protected TXNObject.L
       
        If GetControlProperty(GadgetID(EditorID), $50555245, $54584F42, 4, 0, @TXNObject) = 0
          StartOfLine = FindString(GetGadgetText(EditorID), GetGadgetItemText(EditorID, LineNumber)) - 1
          EndOfLine = StartOfLine + Len(GetGadgetItemText(EditorID, LineNumber))
          TXNSetSelection(TXNObject, StartOfLine, EndOfLine)
          TXNShowSelection(TXNObject, #True)
        EndIf
      CompilerElse
        Protected Range.NSRange
       
        Range\location = FindString(GetGadgetText(EditorID), GetGadgetItemText(EditorID, LineNumber)) - 1
        Range\length = Len(GetGadgetItemText(EditorID, LineNumber))
        CocoaMessage(0, GadgetID(EditorID), "setSelectedRange:@", @Range)
        CocoaMessage(0, GadgetID(EditorID), "scrollRangeToVisible:@", @Range)
      CompilerEndIf
    CompilerCase #PB_OS_Windows
      Protected LineStart.I

      LineStart = SendMessage_(GadgetID(#Editor), #EM_LINEINDEX, LineNumber, 0)
      SendMessage_(GadgetID(#Editor), #EM_SETSEL, LineStart, LineStart + Len(GetGadgetItemText(#Editor, LineNumber)))
  CompilerEndSelect
EndProcedure


OpenWindow(0, 100, 100, 300, 200, "Find clicked text in Editor")
EditorGadget(#Editor, 10, 10, 280, 180, #PB_Editor_ReadOnly)

AddGadgetItem(#Editor, -1, "Some text in EditorGadget")
AddGadgetItem(#Editor, -1, "Next text in EditorGadget")
AddGadgetItem(#Editor, -1, "text")
AddGadgetItem(#Editor, -1, "Some text in EditorGadget")
AddGadgetItem(#Editor, -1, "Next text in EditorGadget")
AddGadgetItem(#Editor, -1, "text")

SelectEditorLine(#Editor, 5) ; try to select lines 2, 3, 4, 5 -- not working on Mac OS X using Cocoa

Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      Break
  EndSelect
ForEver
SelectEditorLine(#Editor, 5) does not select line 5 on MacOSX Cocoa as one would think. It selects other text parts.
Still good code, just not generally useable as is.
User avatar
Shardik
Addict
Addict
Posts: 1991
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Re: Move to a position in an EditGadget

Post by Shardik »

Danilo wrote:marcoagpinto was just looking for code to set the cursor position, not selecting a line. It's different, and maybe
he didn't know how to do it himself, so he asked.
That's right. Selecting a line in an EditorGadget is not the same as selecting a single word. So my solution with selecting a complete line doesn't fit completely. But nevertheless utilizing my cross-platform line selection would have been a good starting point. And I tried to demonstrate that by using my already posted and (nearly) unchanged procedure SelectEditorLine(), a solution without any further API functions would have been quite easy... :wink:
Danilo wrote:Your procedure SelectEditorLine(EditorID.I, LineNumber.I) has some problems on MacOS X. It works in your special example,
but it is not useable for general purposes.
Totally correct! And I have to apologise for not taking a second look into the Mac part when simply pasting SelectEditorLine() into my new code. Then I would have seen at once that a second FindString() in SelectEditorLine() couldn't have worked on a general basis at all. The Mac part was crude and only works under the special circumstances of selecting one line after another... :oops:

Thank you for even taking the time and posting a demo code demonstrating the problems in the Mac part!

To elimainate the Mac problems I have changed

Code: Select all

    CompilerCase #PB_OS_MacOS
      CompilerIf #PB_Compiler_Version <= 461 Or (#PB_Compiler_Version >= 500 And Subsystem("Carbon"))
        Protected EndOfLine.L
        Protected StartOfLine.L
        Protected TXNObject.L
       
        If GetControlProperty(GadgetID(EditorID), $50555245, $54584F42, 4, 0, @TXNObject) = 0
          StartOfLine = FindString(GetGadgetText(EditorID), GetGadgetItemText(EditorID, LineNumber)) - 1
          EndOfLine = StartOfLine + Len(GetGadgetItemText(EditorID, LineNumber))
          TXNSetSelection(TXNObject, StartOfLine, EndOfLine)
          TXNShowSelection(TXNObject, #True)
        EndIf
      CompilerElse
        Protected Range.NSRange
       
        Range\location = FindString(GetGadgetText(EditorID), GetGadgetItemText(EditorID, LineNumber)) - 1
        Range\length = Len(GetGadgetItemText(EditorID, LineNumber))
        CocoaMessage(0, GadgetID(EditorID), "setSelectedRange:@", @Range)
        CocoaMessage(0, GadgetID(EditorID), "scrollRangeToVisible:@", @Range)
      CompilerEndIf
to

Code: Select all

    CompilerCase #PB_OS_MacOS
      CompilerIf #PB_Compiler_Version <= 461 Or (#PB_Compiler_Version >= 500 And Subsystem("Carbon"))
        Protected EndOfLine.L
        Protected i.I
        Protected StartOfLine.L
        Protected TXNObject.L
        
        If LineNumber > 0
          For i = 0 To LineNumber - 1
            StartOfLine + Len(GetGadgetItemText(EditorID, i)) + 1
          Next i
        EndIf
        
        If GetControlProperty(GadgetID(EditorID), $50555245, $54584F42, 4, 0, @TXNObject) = 0
          EndOfLine = StartOfLine + Len(GetGadgetItemText(EditorID, LineNumber))
          TXNSetSelection(TXNObject, StartOfLine, EndOfLine)
          TXNShowSelection(TXNObject, #True)
        EndIf
      CompilerElse
        Protected i.I
        Protected Range.NSRange
        
        If LineNumber > 0
          For i = 0 To LineNumber - 1
            Range\location + Len(GetGadgetItemText(EditorID, i)) + 1
          Next i
        EndIf
        
        Range\length = Len(GetGadgetItemText(EditorID, LineNumber))
        CocoaMessage(0, GadgetID(EditorID), "setSelectedRange:@", @Range)
        CocoaMessage(0, GadgetID(EditorID), "scrollRangeToVisible:@", @Range)
in my complete code example above. It's still crude by calculating the actual start position of a line by iterating through all lines before that line adding up their respective lengths, so there is still the opportunity for improvements... :wink:
Post Reply