News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

SlimViewer, project in MasmBasic

Started by LordAdef, January 26, 2018, 06:32:58 PM

Previous topic - Next topic

LordAdef

Hi JJ,

I´m starting this thread for that side project I told you about.

well, I managed to get a Combobox, although I have no clue of what I´m doing :dazzled:

So far, this is what I get:
   include \masm32\MasmBasic\Res\MbGui.asm
Init

   Let esi=FolderOpen$()      ; no args = use Windows defaults, start with current folder
      
   ;GuiControl MyEdit, "richedit", "Go to the menu and open a file", y80+20
   
   ; ------------------- StatusBar  ------------------------
   GuiControl MySbar, "statusbar", text "MasmBasic is easy"

   ; ------------------- ComboBox ------------------------
      GuiControl MyLb, "combobox", y 60, h 100
      GuiControl MyEd, "static", text "Double-click to open a file ", h 50
      GetFiles esi *.as?
      ;SortFiles      ; latest files on top
      SetListbox Files$()      ; Fill the listbox with UTF-8 encoded file names
      ; SetCombobox Files$()      ; This gives me an error
      Event Command
      .if NotifyCode==LBN_DBLCLK
      ; this gives me an error
      ;uShEx LbSel$      
; open selected file with ShellExecuteW (u=assume UTF-8 for LbSel$)
      .endif
      GuiEnd
EndOfCode[/quote]

SetComboBox gives me this: "Symbol not defined : StringArrHeader.fSize"

Firstly, How do I set the windows´ size?
Secondly: if GetFiles *.as?   ; asc, asm, rc    has no full address, does it give me the file list from where my .exe is running? In case of a plugin, the folder of the current opened .asc?
Thirdly, it seems GuiControl still hasn´t got treeview implemented. Right?

BTW, MB is fun!

jj2007

Quote from: LordAdef on January 26, 2018, 06:32:58 PM
SetComboBox gives me this: "Symbol not defined : StringArrHeader.fSize"
That is a bug, thanks for finding it ;); see code below for instructions.

QuoteFirstly, How do I set the windows´ size?

GuiParas equ "Hello LordAdef", x650, y20, w200, h200, cblack, b00FFFFD0h
GuiMenu equ @File, &Open, &Save, -, E&xit, @Edit, Undo, Copy, Paste
include \masm32\MasmBasic\Res\MbGui.asm
Event Menu
  deb 1, "You clicked", MenuID
Event Message
  inc msgCount
  deb 4, "msg", chg:msgCount    ; , wParam, lParam      ; see deb
GuiEnd


QuoteSecondly: if GetFiles *.as?   ; asc, asm, rc    has no full address, does it give me the file list from where my .exe is running? In case of a plugin, the folder of the current opened .asc?
Yes. But you can construct other paths with Cat$(), see below.

QuoteThirdly, it seems GuiControl still hasn´t got treeview implemented. Right?

BTW, MB is fun!

Thanks :biggrin:

GuiControl might work with a treeview, but I never tested it. Try GuiControl MyTree, "SysTreeview32", style TVS_HASLINES
If you don't see error messages, you have a treeview control with handle hMyTree. All the rest has to be done manually, e.g. with the TVM_INSERTITEM message.


   include \masm32\MasmBasic\Res\MbGui.asm
; Init ; not needed for the MbGui family

    Let esi=FolderOpen$()      ; no args = use Windows defaults, start with current folder
       
    ;GuiControl MyEdit, "richedit", "Go to the menu and open a file", y80+20
   
    ; ------------------- StatusBar  ------------------------
    GuiControl MySbar, "statusbar", text "MasmBasic is easy"

   ; ------------------- ComboBox ------------------------
      GuiControl MyLb, "combobox", y 60, h 100
      GuiControl MyEd, "static", text "Double-click to open a file ", h 50
      GetFiles Cat$(esi+"\*.as?")
      deb 4, "files found", eax ; use console output to verify what's happening!
      ;SortFiles      ; latest files on top
      ; SetListbox Files$()      ; Fill the listbox with UTF-8 encoded file names
      SetCombobox Files$()      ; This gives me an error -> in \masm32\MasmBasic\Res\MbGui.asm, change StringArrHeader.fSize to StringArrHeader.AnsiSize

      Event Command
      .if NotifyCode==CBN_SELCHANGE
    ; this gives me an error
    PrintLine "[", CbSel$, "]" ; use console output to verify what's happening!
    wShEx wRec$(CbSel$)      ; open selected file with ShellExecuteW (u=assume UTF-8 for LbSel$)
      .endif
      GuiEnd

LordAdef

Would you give me an example, one parent? I got a bit lost

jj2007

Strangely enough, I have never used a TreeView before. Anyway, I got it working (project attached):

GuiParas equ "TreeView demo", x650, y20, w200, h400, cblack, b00FFFFD0h
include \masm32\MasmBasic\Res\MbGui.asm
GuiControl MyTree, "SysTreeview32", style TVS_HASLINES or TVS_SINGLEEXPAND, bcol RgbCol(255, 240, 0)
  SetGlobals tvi:TV_INSERTSTRUCT
  m2m tvi.hParent, TVI_ROOT
  mov tvi.hInsertAfter, TVI_LAST
  m2m tvi.item.imask, TVIF_TEXT
  mov tvi.item.pszText, Chr$("I am the root")
  mov tvi.hParent, rv(SendMessage, hMyTree, TVM_INSERTITEM, 0, addr tvi)

  For_ ct=1 To 29
        mov tvi.item.pszText, Str$("This is line %i", ct)
        invoke SendMessage, hMyTree, TVM_INSERTITEM, 0, addr tvi
  Next
GuiEnd


To see the nodes, double-click on "I am the root".

LordAdef

I cannot build you example. Error in line #3.

jj2007

Comment out ; bcol... (the background colour doesn't work anyway with this control)

LordAdef

#6
Yes, thanks!

So, code:



; --------------------------------------------------------------------------------------------------------------------------
;                                                      Window / Menu  / StatusBar
; --------------------------------------------------------------------------------------------------------------------------
GuiParas equ "SlimViewer", x650, y20, w800, h900, cblack,  b 0EEEFFFh ; Last item == Bk colour
GuiMenu equ @File, &Open, &Save, -, E&xit, @Edit, Undo, Copy, Paste
include \masm32\MasmBasic\Res\MbGui.asm ; It MUST go after GuiMenu. WHY JJ??
GuiControl MySbar, "statusbar", text "MasmBasic is easy"

    ; --------------------------------------------------------------------------------------------------------------------------
;                                                        ComboBox
; --------------------------------------------------------------------------------------------------------------------------
Let esi=FolderOpen$()

GuiControl MyEd, "static", x 5, y 55, h 24, w200, text "Select a file: " ; Label
GuiControl MyLb, "combobox", x 5, y 80, h 500, w 300 ; no args = use Win defaults, start with current folder
GetFiles Cat$ ("\*.as?")    ; (esi+"\*.as?") for full path
deb 4, "files found", eax ; use console output to verify what's happening!
SortFiles      ; latest files on top
SetCombobox Files$()     
; --------------------------------------------------------------------------------------------------------------------------
;                                                        TreeView
; --------------------------------------------------------------------------------------------------------------------------

GuiParas  equ "TreeView demo", x 70, y 0, w 55, h100, cblack,  b 0EEEEEEh   ;;<== IS THIS DOING ANYTHING ???
GuiControl MyTree, "SysTreeview32", x 5 , y 120, w 300 , h 880 ,  style  WS_BORDER or TVS_SINGLEEXPAND or TVS_LINESATROOT or TVS_HASBUTTONS or TVS_HASLINES or TVS_FULLROWSELECT

SetGlobals tvi:TV_INSERTSTRUCT

;  Root Procedure :
m2m tvi.hParent, TVI_ROOT
mov tvi.hInsertAfter, TVI_LAST
m2m tvi.item.imask, TVIF_TEXT
mov tvi.item.pszText, Chr$("Procedures")
mov tvi.hParent, rv(SendMessage, hMyTree, TVM_INSERTITEM, 0, addr tvi)

For_ ct=1 To 29
mov tvi.item.pszText, Str$("proc %i", ct)
invoke SendMessage, hMyTree, TVM_INSERTITEM, 0, addr tvi
Next

; Root Macros :
m2m tvi.hParent, TVI_ROOT
mov tvi.hInsertAfter, TVI_LAST
m2m tvi.item.imask, TVIF_TEXT
mov tvi.item.pszText, Chr$("Macros")
mov tvi.hParent, rv(SendMessage, hMyTree, TVM_INSERTITEM, 0, addr tvi)

For_ ct=1 To 29
mov tvi.item.pszText, Str$("macros %i", ct)
invoke SendMessage, hMyTree, TVM_INSERTITEM, 0, addr tvi
Next
; --------------------------------------------------------------------------------------------------------------------------
;                                                       RichEdit
; --------------------------------------------------------------------------------------------------------------------------
  GuiControl Overview, "richedit", x 310, y 120, h880, w490


; --------------------------------------------------------------------------------------------------------------------------
;                                                        Events
; --------------------------------------------------------------------------------------------------------------------------

Event Menu
deb 1, "You clicked", MenuID
Event Message
inc msgCount
deb 4, "msg", chg:msgCount    ; , wParam, lParam      ; see deb
Event Command
    .if NotifyCode==CBN_SELCHANGE
    PrintLine "[", CbSel$, "]" ; use console output to verify what's happening!
    wShEx wRec$(CbSel$)      ; open selected file with ShellExecuteW (u=assume UTF-8 for LbSel$)
    .endif
GuiEnd
EndOfCode


1. Why include \masm32\MasmBasic\Res\MbGui.asm must go after GuiMenu and What things wont work preceding the include (it took me a while to figure this was the reason the Menu wasn´t showing up).

2. This line doesn´t seem to be doing anything:  GuiParas  equ "TreeView demo", x 70, y 0, w 55, h100, cblack,  b 0EEEEEEh
    Only when I moved x y h w coord into Gui TreView, it worked


Don´t curse at me JJ, this is a crash course for MasmBasic Noobs! It will serve for others too   :greenclp: :bgrin::eusa_dance:

LordAdef

Next step is to search for proc names within the selected file.

I saw many possible instructions: FileRead$, FileOpen$, Open.  Seek,  ArraySearch and Trim looked useful.
What´s the best MB way of doing this?

jj2007

Quote from: LordAdef on February 03, 2018, 01:19:29 PM
1. Why include \masm32\MasmBasic\Res\MbGui.asm must go after GuiMenu and What things wont work preceding the include (it took me a while to figure this was the reason the Menu wasn´t showing up).

The GuiParas and GuiMenu lines must come before the include simply because the internal WinMain proc must know what are the required settings. The include does its initialisation stuff then, and directly after you are right in the WM_CREATE handler, with GuiControl ... and similar.

Quote2. This line doesn´t seem to be doing anything:  GuiParas  equ "TreeView demo", x 70, y 0, w 55, h100, cblack,  b 0EEEEEEh
The GuiParas equate appears once before the include line. If it comes after that line, it will be ignored.

QuoteDon´t curse at me JJ, this is a crash course for MasmBasic Noobs! It will serve for others too   :greenclp: :bgrin::eusa_dance:
;)

Quote from: LordAdef on February 03, 2018, 04:14:50 PM
Next step is to search for proc names within the selected file.

I saw many possible instructions: FileRead$, FileOpen$, Open.  Seek,  ArraySearch and Trim looked useful.
What´s the best MB way of doing this?
Let esi=FileRead$(filename) gives you a fat buffer with file content. No need for Open or Seek then.
FileOpen$ gives you the dialog, not what you want here I suppose.
ArraySearch is a very specialised binary search; what you need here is Instr_(src, pattern, mode).

Take a look at Recall. Since you need to find things like MyTest proc args, your needs are more line-oriented:
Recall "thefile.txt", L$()
For_ ct=0 to L$(?)-1
  Let esi=L$(ct)
  .if Instr_(esi, "proc", 5)  ; mode 5: 1=case-insensitive, 4=full word
     PrintLine esi, " contains a proc"
     push esi
     ; from here on, you may need some "real" assembler examining esi ;-)
     pop esi
  .endif
Next


Note that Let esi=L$(ct) in a loop is legal code but you are responsible that esi is unchanged when you use Let esi= again in the next iteration, therefore the push esi/pop esi sequence.

LordAdef

So,

"For_ ct= 0 to lineMax -1"  gives "Error A2209: Count must be positive or zero"

I added the following lines:

SetGlobals lineMax:DWORD
mov lineMax, eax ; Min(eax, 20)  **** Number of lines ret in eax ****
                Print Str$("%i lines found in File ", lineMax)

to make sure Recall was getting it right. It was.

1. What´s happening JJ?

2. Can you set a variable without declaring it ? I used SetGlobals."Let lineMax = eax" is wrong too

3. We don´t need Init or include Masmbasic when using MBGui.asm, right? (I do know this one, but it´s good for the tutorial)

    ; --------------------------------------------------------------------------------------------------------------------------
;                                                        .... RECALL ....
; --------------------------------------------------------------------------------------------------------------------------

Recall "\masm32\AFprojs\__slimViewer\template.asc", L$()    ; Hard coded to test the functions


SetGlobals lineMax:DWORD
mov lineMax, eax ; Min(eax, 20)
                  Print Str$("%i lines found in File ", lineMax)
                   
For_ ct= 0 to lineMax -1
Let esi=L$(ct)
.if Instr_(esi, "proc", 5)  ; mode 5: 1=case-insensitive, 4=full word
  PrintLine esi, " contains a proc"
   push esi
;;  from here on, you may need some "real" assembler examining esi ;-)
   pop esi
.endif
Next



jj2007

Quote from: LordAdef on February 04, 2018, 08:43:04 AM
"For_ ct= 0 to lineMax -1"  gives "Error A2209: Count must be positive or zero"
Check if lineMax is defined.

Quote2. Can you set a variable without declaring it ? I used SetGlobals."Let lineMax = eax" is wrong too
Normally not, but For_ ct=0 To whatever is an exception: no need to declare ct (but a REAL8 counter must be declared).
Let lineMax=eax won't work because Let is only for strings. Use mov lineMax, eax

Quote3. We don´t need Init or include Masmbasic when using MBGui.asm, right?
Correct, the Init is done internally. Here is full code with the right order of SetGlobals and Recall:

include \masm32\MasmBasic\Res\MbGui.asm
  Recall "\Masm32\include\Windows.inc", L$()
  SetGlobals lineMax
  deb 4, "lines", eax
  mov lineMax, eax
  deb 4, "lines", lineMax
  For_ ecx=0 To Min(20, lineMax-1)
        PrintLine L$(ecx)
  Next

Event Key
  deb 1, "You pressed", VKey
GuiEnd

LordAdef

Ok,

I´m using "endp" to fetch my procedure names. For now I don´t care if I´m getting false positives. All I want is a draft with functionality.

Recall "\masm32\AFprojs\__slimViewer\Text.asm", L$() ; Hard coded only to test the instructions
mov lineMax, eax ; Min(eax, 20)
For_ ct= 0 To lineMax    ; Min(1000, lineMax-1)   ;
Let esi=L$(ct)

.if Instr_(esi, "endp", 5)  ; mode 5: 1=case-insensitive, 4=full word
; returns relative pos in edx, absolute in eax
sub edx, 2 ; " e"(ndp)
mov eax, z$(Left$(esi, edx))
; PrintLine Left$(esi, edx)
deb 4, "¨", $eax
push esi
mov tvi.item.pszText, Chr$(byte ptr [eax])
invoke SendMessage, hMyTree, TVM_INSERTITEM, 0, addr tvi
pop esi
.endif
Next


- Instr_ returns me the relative position of "endp" in edx. Cool. 
- I "sub edx, 2" to get rid of " e"(from " endp") and what´s left is my proc name
- which can be saved into eax with this:   mov eax, z$(Left$(esi, edx))    
- deb 4, "¨", $eax  outputs all my target names!

- eax has it. I can insert the first byte of each procedure name: mov tvi.item.pszText, Chr$(byte ptr [eax])

I could easily get the full name with asm. But the idea is to be as MB as possible. So, how can I retrieve the whole string (pointed by eax)?



jj2007

Your code works fine, Alex. A few remarks, as shown below:
- on entry into a For_ loop, you can use whatever register, including eax;
- instead of Let esi=L$(ct), you can use mov esi, L$(ct), thus avoiding to heapalloc an extra string. However, esi should be treated as a read-only pointer.
- Instr_() returns both the index in edx, and a pointer to the match in eax. In your case, the pointer is easier to use, see below.

include \masm32\MasmBasic\Res\MbGui.asm
  SetGlobals lineMax, tvi:TV_INSERTSTRUCT
  Recall "Whatever.asm", L$()     ; change
  mov lineMax, eax
  deb 4, "lines", lineMax
  For_ ecx=0 To eax-1           ; instead of lineMax, you can use eax
        mov esi, L$(ecx)
        .if Instr_(esi, "endp", 1+4)    ; case-insensitive, full word
                .Repeat
                        dec eax
                .Until byte ptr [eax]>="0"      ; find the tail of sometest<whitespace>endp
                mov edx, eax
                .Repeat
                        dec eax
                .Until byte ptr [eax-1]<=32     ; find the start of  CrLf<whitespace>sometest endp
                sub edx, eax     ; tail-start=#bytes
                inc edx
                mov tvi.item.pszText, z$(Left$(eax, edx))
                PrintLine "[", eax, "]"  ; replace with SendMessage, ...
        .endif
  Next

Event Timer    ; at least one event is needed
  Print "."
GuiEnd


8)

LordAdef

Nice Johen.
I made a small change in your code: For_ ecx ...  ecx gives error. I PUSHED and used edi instead and it worked fine
I filled the 3 Parents just for fun (it IS fun!). See the zip.
For that reason lineMax is good, for I can retrieve back into eax, for every parent feeding

Quote                                                   PUSH edi
                                                   mov eax, lineMax
                                                 For_ edi=0 To eax-1           ; instead of lineMax, you can use eax
                                                     mov esi, L$(edi)
                                                      .if Instr_(esi, "equ", 1+4)    ; case-insensitive, full word
                                                               deb 4, "¨", eax
                                                             .Repeat
                                                                      dec eax
                                                              .Until byte ptr [eax]>="0"      ; find the tail of sometest<whitespace>endp
                                                             mov edx, eax
                                                              .Repeat
                                                                      dec eax
                                                              .Until byte ptr [eax-1]<=32    ; find the start of  CrLf<whitespace>sometest endp
                                                             sub edx, eax    ; tail-start=#bytes
                                                             inc edx
                                                             
                                                              PUSH esi
                                                             mov tvi.item.pszText, z$(Left$(eax, edx))
                                                              invoke SendMessage, hMyTree, TVM_INSERTITEM, 0, addr tvi
                                                          ;  PrintLine "[", eax, "]" ; replace with SendMessage, ...
                                                            POP esi
                                                      .endif
                                                Next
                                                POP edi

For the tutorial sake, here are the following questions and remarks I believe are relevant at this point:

1. It´s possible to create your own .data segment. I placed mine right below  "include \masm32\MasmBasic\Res\MbGui.asm" and it all seemed to be ok.
    Using the lineMax variable as an example, what would you choose: SetGlobals vs .data?

Quote         include \masm32\MasmBasic\Res\MbGui.asm                        ; It MUST go after GuiMenu. WHY JJ??

.data?
   lineMax      dd  ?
.code 
        ;SetGlobals lineMax:DWORD
        GuiControl Status, "statusbar", text "MasmBasic is easy"
Still on this topic, You wrote this: SetGlobals lineMax, tvi:TV_INSERTSTRUCT
   lineMax has no size. Does MB assumes it´s a dword?

2. Recall "\masm32\AFprojs\__slimViewer\Text.asm", L$()
       Is L$() a Global?
       How do we release the Recall (or close the file, free the memory, whatever is under the hood)?
       Say we want to Recall another file, Can we use L$ and overwrite the data in there?
3. Why MbGui.asm has MasmBasic inside and not the other way around?
4. Do you have plans on expanding GuiControls?
5. I forgot about this one: z$. It´s a porweful and useful one, but no reference in the help, Would explain it for us?

jj2007

Quote from: LordAdef on February 05, 2018, 07:22:18 AMI made a small change in your code: For_ ecx ...  ecx gives error.
MB protects ecx, but any WinApi call will trash it. So
push ecx
invoke SendMessage, ...
pop ecx

is one option. Or use For_ ct=..., i.e. a global counter.

QuoteFor the tutorial sake, here are the following questions and remarks I believe are relevant at this point:
1. It´s possible to create your own .data segment. I placed mine right below  "include \masm32\MasmBasic\Res\MbGui.asm" and it all seemed to be ok.
    Using the lineMax variable as an example, what would you choose: SetGlobals vs .data?
The best option is to use local variables, but if that is not possible:
- SetGlobals is easy to declare and uses the .data? section for frequently used variables with short encodings of the form
mov eax, [ebx+n]. Advantage: compact code. Disadvantage: under the hood, it needs ebx as an index register, so ebx is not available for other things unless you push+pop it.
- "Real" global variables have longer encodings, 5 bytes instead of 3. Good for rarely used variables that must be globally available.

Quoteinclude \masm32\MasmBasic\Res\MbGui.asm MUST go after GuiMenu. WHY JJ??
GuiParas and GuiMenu are equates that are being used by MbGui.asm. So they must be defined before the include.

QuoteStill on this topic, You wrote this: SetGlobals lineMax, tvi:TV_INSERTSTRUCT
lineMax has no size. Does MB assumes it´s a dword?
Yes, exactly, but it's not MB-specific. Masm does the same for local variables and args of a proc.

Quote2. Recall "\masm32\AFprojs\__slimViewer\Text.asm", L$()
       Is L$() a Global?
When using Recall "some.txt", some$(), you get an array of strings, and arrays are always global.

QuoteHow do we release the Recall (or close the file, free the memory, whatever is under the hood)?
No need to release it. If you are short of memory, you may use Erase$ some$(), but normally it's not needed.

QuoteSay we want to Recall another file, Can we use L$ and overwrite the data in there?
Yes, the old array gets automatically heapfree'd, and a new one with the same name gets created.

Quote3. Why MbGui.asm has MasmBasic inside and not the other way around?
4. Do you have plans on expanding GuiControls?
3. MbGui is newer and relies on the full MB library.
4. In principle, GuiControls should already work with all kind of controls, but some are special. Right now I am working on a spreadsheet/table/grid control (the name is not yet decided).

Quote5. I forgot about this one: z$. It´s a porweful and useful one, but no reference in the help, Would explain it for us?
For Print and Let, MB uses an internal buffer for short strings, which allows e.g.
Let esi="Hello, now it's "+fTime$(0)+" on "+fDate$(0, "dd MMM yy")
The Let and Print macros have access to the internal structure that registers the order of the strings etc, and can thus produce compact and powerful code.
In rare occasions, you may want a direct pointer, example:
  mov eax, z$(Left$("abcde", 3))
  PrintLine eax

I haven't used z() for years, though. Be careful, check if it really works, and use Olly if in doubt. There is a reason why it is not documented; my advice: Use Cat$() instead, it's safer.