uglobal
  hProjManager    dd 0  ; handle of project manager
  ptrProjectFile  dd 0  ; pointer to TOpenFile with ftProject, currently open in the project manager.
  fProjModified   dd 0  ; flag "modified" for current open project.

  poMemory       dd 0  ; The memory size for the compiler in KB!
  poBinNameAuto  dd 0  ; The flag whether the binary name will be auto created of fixed
  poBinName      dd 0  ; Handle to the binary name.
  poVarArray     dd 0  ; points to the array with environment variables for the project.

  imlCategories   dd 0  ; Image list for tree-view control

  ptrMRUProj      dd 0  ; pointer to MRU list for projects.

  hTreePopup      dd 0
  hListPopup      dd 0
  hSettingsPopup  dd 0
  hSettingsString dd 0

  ptrMemoryAllocated dd ?
endg


iglobal
  cProjTitle du 'Project manager', 0
endg

cSettingsCaption text 'Settings: ', 0


iglobal
  CoolMenu mnuProjTree,           \
    mfNormal, actAddCategory,     \
    mfNormal, actEditCategory,    \
    mfNormal, actDelCategory,     \
    mfSeparator, NONE,            \
    mfNormal, actMoveCategoryUp,  \
    mfNormal, actMoveCategoryDn

  CoolMenu mnuProjList,         \
    mfNormal, actAddFile,       \
    mfNormal, actDelFile

  CoolMenu mnuSettings,         \
    mfSeparator, NONE,          \
    mfNormal, actProjectOptions



  SplitStart ProjLayout

    Split stVert or stJustGap, 2, 24, 24, 24
      Cell cellSettings
      Split stVert or stJustGap, 2, 24, 0, 48
        Cell cellMain
        Split stVert or stRelative, 4, 10000, 0, 32768
          Cell cellCategories
          Cell cellFiles

  SplitEnd



endg




;----------------------------------------
; Creates project manager common objects.
; Call this only once.
;----------------------------------------
proc InitProjectManager
begin
; Read project MRU list
        stdcall LoadMRUFromIni, 16, [hCfgFileName], cMRUProjSection
        mov     [ptrMRUProj], eax

        stdcall ImageList_LoadGif, [hInstance], resBmpProjMan, 16, FALSE
        mov     [imlCategories], eax

        stdcall CreateCoolMenu, mnuProjTree, TRUE
        mov     [hTreePopup], eax

        stdcall CreateCoolMenu, mnuProjList, TRUE
        mov     [hListPopup], eax

        stdcall CreateCoolMenu, mnuSettings, TRUE
        mov     [hSettingsPopup], eax

        mov     [poBinNameAuto], TRUE
        stdcall SetCompilerMemorySize, cDefaultCompilerMemory

        stdcall StrNew
        mov     [poBinName], eax

        return
endp


proc FinalizeProjectManager
begin
        cmp     [ptrProjectFile], NULL
        je      .closeproj

        stdcall CloseFile, [ptrProjectFile]

.closeproj:
        stdcall SaveMRUToIni, [ptrMRUProj], [hCfgFileName], cMRUProjSection
        stdcall DestroyMRUList, [ptrMRUProj]
        invoke  ImageList_Destroy, [imlCategories]

        invoke  DestroyMenu, [hTreePopup]
        invoke  DestroyMenu, [hListPopup]
        invoke  DestroyMenu, [hSettingsPopup]

        stdcall SetCompilerMemorySize, 0        ; free the compiler memory
        return
endp


proc CreateProjectManager, .ptrOpenFile, .hTemplate
begin
        push    ebx esi
        xor     ebx, ebx

        mov     esi, [.ptrOpenFile]

        cmp     [ptrProjectFile], ebx
        je      .onlyone

        stdcall CloseFile, [ptrProjectFile]
        cmp     eax, cfrDestroyed
        jne     .fault

.onlyone:
        mov     [ptrProjectFile], esi

; Initial project options... they could be changed after CEM_LOADFILE

        stdcall SetCompilerMemorySize, cDefaultCompilerMemory
        mov     [poBinNameAuto], TRUE

        cmp     [poBinName], 0
        je      @f
        stdcall StrDel, [poBinName]
@@:
        stdcall StrNew
        mov     [poBinName], eax

        invoke   CreateWindowExW, WS_EX_CONTROLPARENT or WS_EX_ACCEPTFILES, cFormClassName, 0,                   \
                                 WS_CHILD or WS_VISIBLE or WS_TABSTOP or WS_CLIPSIBLINGS or WS_CLIPCHILDREN,    \
                                 ebx, ebx , 200, 400,                                                           \
                                 [hMainForm], ebx, [hInstance], ebx

        mov     [hProjManager], eax
        mov     [esi+TOpenFile.hEditor], eax
        mov     [esi+TOpenFile.projectID], ebx
        stdcall SubclassWindow, eax, ProjManProc

        invoke  SendMessageW, [hProjManager], CEM_LOADFILE, [.hTemplate], ebx

.finish:
        pop     esi ebx
        return

.fault:
        mov     eax, errCanceled
        pop     esi ebx
        return
endp



VarMenuBaseID = 20

proc CreateParametersMenu
.mii MENUITEMINFO
.pos dd ?
.pos_in_list dd ?
.buffer rw 256
begin
        push    ebx ecx esi

        xor     eax, eax
        cmp     [hSettingsString], eax
        jne     @f
        stdcall StrDel, [hSettingsString]
@@:
        stdcall StrDup, cSettingsCaption
        mov     [hSettingsString], eax

; first clear to the first separator.
.deleteloop:
        lea     ebx, [.mii]
        mov     [ebx+MENUITEMINFO.cbSize], sizeof.MENUITEMINFO
        mov     [ebx+MENUITEMINFO.fMask], MIIM_TYPE
        mov     [ebx+MENUITEMINFO.hSubMenu], 0
        lea     eax, [.buffer]
        mov     [ebx+MENUITEMINFO.dwTypeData], eax
        mov     [ebx+MENUITEMINFO.cch], 256
        invoke  GetMenuItemInfoW, [hSettingsPopup], 0, TRUE, ebx
        test    [ebx+MENUITEMINFO.fType], MFT_SEPARATOR
        jnz     .insertvalues

        invoke  DeleteMenu, [hSettingsPopup], 0, MF_BYPOSITION
        jmp     .deleteloop

; then check for multivalue elements in poVarArray
.insertvalues:
        mov     [.pos], 0
        mov     [.pos_in_list], VarMenuBaseID

        mov     esi, [poVarArray]
        test    esi, esi
        jz      .finish

        mov     ecx, [esi+TArray.count]
        lea     esi, [esi+TArray.array]

        cmp     ecx, 0
        je      .finish

.arrayloop:
        stdcall StrCharPos, [esi+TEnvVarType.pValue], '|'       ; check for separator.
        test    eax, eax
        jz      .next_item

        push    ecx
        mov     ecx, eax

        stdcall CreatePopupFromString, [esi+TEnvVarType.pValue], [.pos_in_list]
        mov     [ebx+MENUITEMINFO.hSubMenu], eax

; create menu string.
        mov     byte [ecx], 0
        stdcall StrFixLen, [esi+TEnvVarType.pValue]

        stdcall StrDup, [esi+TEnvVarType.pName]
        push    eax

        stdcall StrCharCat, eax, 09
        stdcall StrCat, eax, [esi+TEnvVarType.pValue]

        stdcall StrCat, [hSettingsString], [esi+TEnvVarType.pValue]
        stdcall StrCharCat, [hSettingsString], ','
        stdcall StrCharCat, [hSettingsString], ' '

        mov     byte [ecx], '|'
        stdcall StrFixLen, [esi+TEnvVarType.pValue]

; insert menu item

        mov     [ebx+MENUITEMINFO.cbSize], sizeof.MENUITEMINFO
        mov     [ebx+MENUITEMINFO.fMask], MIIM_TYPE or MIIM_SUBMENU
        mov     [ebx+MENUITEMINFO.fType], MFT_STRING

        stdcall utf8ToWideChar, eax
        stdcall StrDel ; from the stack

        mov     [ebx+MENUITEMINFO.dwTypeData], eax
        mov     [ebx+MENUITEMINFO.cch], 0
        invoke  InsertMenuItemW, [hSettingsPopup], [.pos], TRUE, ebx
        inc     [.pos]

        stdcall FreeMem, [ebx+MENUITEMINFO.dwTypeData] ; from the stack.

        pop     ecx

.next_item:
        add     esi, sizeof.TEnvVarType
        inc     [.pos_in_list]
        dec     ecx
        jnz     .arrayloop

.finish:
        invoke  GetDlgItem, [hProjManager], ProjSettingsID
        invoke  RedrawWindow, eax, 0, 0, RDW_INVALIDATE

        pop     esi ecx ebx
        return
endp



proc CreatePopupFromString, .hString, .baseid
.mii MENUITEMINFO
.pos dd ?
.wide dd ?
begin
        push    ebx ecx esi edi

        invoke  CreatePopupMenu
        mov     edi, eax

        stdcall utf8ToWideChar, [.hString]
        mov     [.wide], eax
        mov     esi, eax

        lea     ebx, [.mii]

        mov     [.pos], 0

.stringsplit:
        mov     eax, [.baseid]
        shl     eax, 8
        add     eax, [.pos]

        mov     [ebx+MENUITEMINFO.wID], eax
        mov     [ebx+MENUITEMINFO.cbSize], sizeof.MENUITEMINFO
        mov     [ebx+MENUITEMINFO.fMask], MIIM_TYPE or MIIM_ID
        mov     [ebx+MENUITEMINFO.fType], MFT_STRING
        mov     [ebx+MENUITEMINFO.dwTypeData], esi
        mov     [ebx+MENUITEMINFO.cch], 0
        mov     [ebx+MENUITEMINFO.hSubMenu], 0

        xor     eax, eax

.searchloop:
        lodsw
        cmp     ax, '|'
        je      .splithere
        cmp     ax, 0
        jne      .searchloop

.splithere:
        push    eax

        mov     word [esi-2], 0

        invoke  InsertMenuItemW, edi, 0, TRUE, ebx
        inc     [.pos]

        pop     eax

        mov     [esi-2], ax
        cmp     eax, 0
        jne     .stringsplit

        stdcall FreeMem, [.wide]

        mov     eax, edi
        pop     edi esi ecx ebx
        return
endp





msgPUC1 text 'The file: "'
msgPUC2 text '" is part of saved project, but it doesn''t exists on the disk.', 13, 10, \
               'Do you want to add it to the project anyway?'


proc ProjectInconsistent, .ptrOpenFile
begin
        push    ebx
        mov     ebx, [.ptrOpenFile]

        stdcall StrDup, msgPUC1
        stdcall StrCat, eax, [ebx+TOpenFile.hFileName]
        stdcall StrCat, eax, msgPUC2
        push    eax
        stdcall StrPtr, eax

        stdcall AskQuestion, eax, [hMainForm], answerYesNo
        stdcall StrDel ; from the stack
        pop     ebx
        return
endp




;-----------------------------------------------------
; Dialog proc for Project manager.
;-----------------------------------------------------
proc ProjManProc, .hwnd, .wmsg, .wparam, .lparam

.tvi    TVINSERTSTRUCT
.lvi    LVITEM
.col    LVCOLUMN
.count  dd ?
.str    rw 1024
.ptr    dd ?
.pnt    POINT

begin

        mov     ebx, [.wmsg]
        dispatch ebx

.ondefault:
        stc
        return


oncase PMM_FILECHANGED

        stdcall SearchForFile, [.hwnd], [.wparam]
        cmp     eax, -1
        je      .endchanged

        push    eax

        invoke  SendMessageW, [.hwnd], PMM_REMOVEFILE, [.wparam], TRUE ; do not free the structure
        invoke  SendMessageW, [.hwnd], PMM_ADDFILE, [.wparam] ; lparam from the stack

.endchanged:
        clc
        return

oncase PMM_GETCATEGORY       ; (0, hCategory)
locals
  .tvitem TVITEM
  .htree  dd ?
endl

        invoke  GetDlgItem, [.hwnd], ProjTreeCtrlID
        mov     [.htree], eax

        stdcall CategorySearch, eax, [.lparam]
        jc      .not_found
        test    eax, eax
        jz      .not_found

        mov     [.tvitem.mask], TVIF_PARAM
        mov     [.tvitem.hItem], eax

        lea     eax, [.tvitem]
        invoke  SendMessageW, [.htree], TVM_GETITEMW, 0, eax

        mov     esi, [.tvitem.lParam]   ; pointer to TArray of dword with pointers to TOpenFile structures.
        test    esi, esi
        jz      .not_found

        mov     eax, esi
        clc
        return

.not_found:
        xor     eax, eax
        clc
        return

oncase PMM_ADDFILE

        mov     [fProjModified], TRUE

        mov     edi, [.wparam]
        cmp     [edi+TOpenFile.projectID], FALSE
        je      .projok

        OutputValue "Remove from the project: ", edi, 16, 8

        invoke  SendMessageW, [.hwnd], PMM_REMOVEFILE, edi, TRUE  ; TRUE in lparam means don't free the file structure.

.projok:
        cmp     [.lparam], 0
        jne     .addtocategory

; Add as main file.

        cmp     [edi+TOpenFile.ptrType], ftMainSource
        je      .typeok

        cmp     [edi+TOpenFile.ptrType], ftCommonSource
        jne     .erroradd

        mov     [edi+TOpenFile.ptrType], ftMainSource   ; change file type.

.typeok:
        mov     [edi+TOpenFile.projectID], TRUE

; check the previous main file.

.move_old_loop:
        invoke  SendMessageW, [.hwnd], PMM_GETMAINFILE, 0, 0

        OutputValue "Current main file: ", eax, 16, 8

        test    eax, eax
        jz      .prev_main_ok

        OutputValue "Move to categories: ", eax, 16, 8

        invoke  SendMessageW, [.hwnd], PMM_ADDFILE, eax, -1     ; move the previous file to the current selected category.
        jmp     .move_old_loop

.prev_main_ok:
        OutputValue "Add as main file: ", edi, 16, 8

        stdcall AddToListView, [.hwnd], ProjMainCtrlID, edi
        invoke  SendDlgItemMessageW, [.hwnd], ProjMainCtrlID, LVM_SETCOLUMNWIDTH, 0, LVSCW_AUTOSIZE

        stdcall FileChanged, edi, fcEditor
        jmp     .endadd


.addtocategory:
        cmp     [edi+TOpenFile.ptrType], ftMainSource
        jne     .typeok2

        mov     [edi+TOpenFile.ptrType], ftCommonSource

.typeok2:
        cmp     [edi+TOpenFile.projectID], FALSE
        jne     .erroradd

        mov     eax, [.lparam]
        cmp     eax, -1
        jne     .addit

.getcurrent:
        invoke  SendDlgItemMessageW, [.hwnd], ProjTreeCtrlID, TVM_GETNEXTITEM, TVGN_CARET, 0
        test    eax, eax
        jnz     .additfocused

        invoke  SendDlgItemMessageW, [.hwnd], ProjTreeCtrlID, TVM_GETNEXTITEM, TVGN_ROOT, 0
        test    eax, eax
        jnz     .additfocused

        push    edi
        stdcall OnAddCategory, eax, eax
        pop     edi
        test    eax, eax
        jz      .erroradd
        jmp     .getcurrent

.additfocused:
        mov     [.lparam], eax

.addit:
        mov     esi, eax
        stdcall GetTVItemPtr, [.hwnd], esi
        stdcall AddArrayItems, eax, 1
        jc      .erroradd

        stdcall SetTVItemPtr, [.hwnd], esi, edx

        mov     dword [eax], edi
        mov     [edi+TOpenFile.projectID], TRUE

        stdcall FileChanged, edi, fcEditor

        invoke  SendDlgItemMessageW, [.hwnd], ProjTreeCtrlID, TVM_GETNEXTITEM, TVGN_CARET, 0
        cmp     eax, [.lparam]
        jne     .endadd

        stdcall ReloadListView, [.hwnd], [.lparam]

.endadd:
        xor     eax, eax
        clc
        return

.erroradd:

        OutputValue "Error add file of type: ", [edi+TOpenFile.ptrType], 16, 8

        xor     eax, eax
        inc     eax
        clc
        return

oncase PMM_REMOVEFILE

        mov     esi, [.wparam]

        stdcall SearchForFile, [.hwnd], esi
        test    eax, eax
        js      .errdel         ; the file is not in the project.

        mov     [esi+TOpenFile.projectID], FALSE        ; set the file as not in the project

        jz      .mainfile       ; the main is the main file of the project.

        mov     edi, eax
        mov     ebx, edx

        stdcall GetTVItemPtr, [.hwnd], edi
        stdcall DeleteArrayItems, eax, ebx, 1
        stdcall SetTVItemPtr, [.hwnd], edi, edx

        invoke  SendDlgItemMessageW, [.hwnd], ProjTreeCtrlID, TVM_GETNEXTITEM, TVGN_CARET, 0
        cmp     eax, edi
        jne     .enddel

        stdcall ReloadListView, [.hwnd], edi

.enddel:
        mov     eax, fcEditor
        cmp     [.lparam], 0
        je      .notify_changes

        or      eax, fcDontFree

.notify_changes:

        stdcall FileChanged, esi, eax
        mov     [fProjModified], TRUE

        xor     eax, eax
        clc
        return

.mainfile:
        invoke  SendDlgItemMessageW, [.hwnd], ProjMainCtrlID, LVM_DELETEITEM, 0, 0
        jmp     .enddel

.errdel:
        xor     eax, eax
        inc     eax
        clc
        return

oncase CEM_LOADFILE
locals
  .chunkname dd ?
endl
        mov     ebx, [ptrProjectFile]

        cmp     [.wparam], 0
        jne     .loadit

        cmp     [ebx+TOpenFile.fNeverSaved], FALSE
        je      .loadit

        mov     [fProjModified], FALSE
        xor     eax, eax
        clc
        return

.loadit:
        mov     [fProjModified], FALSE
        stdcall ChangeDir, [ebx+TOpenFile.hFileName]

; first set default options.
        stdcall EmptyVarList, poVarArray

; then load it...
        mov     eax, [.wparam]
        test    eax, eax
        jnz     @f
        mov     eax, [ebx+TOpenFile.hFileName]
@@:
        stdcall StrPtr, eax
        stdcall LoadBinaryFile, eax

        mov     [.ptr], eax
        mov     esi, eax

        cmp     ecx, 8
        jl      .invalidfile

; Project file header.
; Check the file singature
        lea     ecx, [ecx-8]

        lodsd
        cmp     eax, 'FNPr'   ; Fresh new project?
        jne     .invalidfile

        lodsd
        cmp     eax, $0a1a0a0d  ;) magic number. 'CR, LF, EOF, LF'
        je      .readchunks

.invalidfile:
        stdcall ErrorMessage, errNotProject
        jmp     .endoffile

.readchunks:
        cmp     ecx, 8
        jl      .endoffile

        sub     ecx, 4
        lodsd
        mov     [.chunkname], eax

        call    .get_chunk_len
        jl      .invalidfile

        call    .check_chunk_hash
        jne     .invalidfile

; now esi points to the begin of the chunk data. edx contains the length of the chunk.
        push    ecx
        push    esi
        push    edx

        cmp     [.chunkname], 'PRTR'
        je      .read_project_tree

        cmp     [.chunkname], 'VARS'
        je      .read_vars

        cmp     [.chunkname], 'MAIN'
        je      .read_main_source_name

        cmp     [.chunkname], 'CMEM'
        je      .read_compiler_memory

        cmp     [.chunkname], 'CTNM'
        je      .read_output_filename

; unknown chunk. simply skip it.
.nextchunk:
        pop      edx
        pop      esi
        pop      ecx

        lea     esi, [esi+edx+4]  ; skip hash, it is already checked.
; adjust file length
        sub     ecx, edx
        sub     ecx, 4          ; 4 more bytes for the hash
        jmp     .readchunks


.endoffile:
        stdcall FreeMem, [.ptr]
        stdcall SetMainTitle

        stdcall CreateParametersMenu

        mov     ebx, [ptrProjectFile]
        stdcall AddToMRUList, [ptrMRUProj], [ebx+TOpenFile.hFileName]

        invoke  SendDlgItemMessageW, [.hwnd], ProjMainCtrlID, LVM_SETCOLUMNWIDTH, 0, LVSCW_AUTOSIZE

        xor     eax, eax
        clc
        return


;*****************************************************************************
;
; Chunk helper procedures.
;*****************************************************************************


;returns data length in EDX and datalen+4 in eax
.get_chunk_len:
        sub     ecx, 4
        lodsd   ; length of the chunk data
        mov     edx, eax
        add     eax, 4
        cmp     ecx, eax
        retn

; esi points to data.
; edx contains data len.
;
; returns computed hash in eax
.check_chunk_hash:
        push    esi edx ecx

        mov     eax, $811C9DC5                  ; 2166136261              ; FNV offset basis
        test    edx, edx
        jz      .endhash

.hashloop:
        movzx   ecx, byte [esi]
        inc     esi
        xor     eax, ecx
        imul    eax, $01000193                  ;   16777619              ; FNV prime
        dec     edx
        jnz     .hashloop

.endhash:
        mov     ecx, [esi]       ; hash from the chunk end
        cmp     eax, ecx
        pop     ecx edx esi
        retn

; esi points to the begin of the data.
; edi points just after the data.

.finalize_chunk:
        push    edx

        mov     edx, edi
        sub     edx, esi
        mov     [esi-4], edx      ; size of the chunk.
        stdcall .check_chunk_hash
        stosd                     ; save chunk hash at the end of the chunk.

        pop     edx
        retn


;--------------------------------------
; edi - pointer to the stream buffer
; eax - handle/pointer to the string
;--------------------------------------
.StrToStream:
        push    ecx
        stdcall StrPtr, eax
        xor     ecx, ecx

.copyname:
        mov     cl, [eax]
        inc     eax
        jecxz   .endofname
        mov     [edi], cl
        inc     edi
        jmp     .copyname

.endofname:
        pop     ecx
        retn






;***************************************************************************
;
; chunk 'PRTR' - contains one project category and its files.
;
;
.read_project_tree:
        lodsd                   ; icon number
        add     esi, 4          ; reserved dword for future use.

        stdcall GetCategoryTree, esi, TVI_ROOT, eax
        mov     ebx, eax        ; Current category tree item.

.filesloop:
        stdcall StrLen, esi
        lea     esi, [esi+eax+1]        ; goto to next file

        cmp     byte [esi], 0
        je      .nextchunk

        stdcall DetectFileType, esi
        stdcall CreateFStruct, esi, eax
        mov     edi, eax

        cmp     [.wparam], 0
        jne     .addtoproject    ; loaded from template.

        stdcall GetModified, edi
        test    eax, eax
        jz      .addtoproject

        stdcall ProjectInconsistent, edi
        cmp     eax, IDNO
        je      .adderror

.addtoproject:
        push    [fProjModified]
        invoke  SendMessageW, [.hwnd], PMM_ADDFILE, edi, ebx
        pop    [fProjModified]
;        mov     [fProjModified], FALSE

        test    eax, eax
        jz      .filesloop

.adderror:
        mov     [fProjModified], TRUE
        stdcall FreeFStruct, edi   ; error add file...
        jmp     .filesloop


;******************************************************************
.read_vars:
        stdcall StrLen, esi
        test    eax, eax
        jz      .nextchunk

        push    esi
        lea     esi, [esi+eax+1]
        stdcall StrDup ; from the stack.
        push    eax             ; key name

        stdcall StrLen, esi

        push    esi
        lea     esi, [esi+eax+1]
        stdcall StrDup ; from the stack.
        push    eax     ; key value.

        stdcall _AddKeyValueToArray, poVarArray ; two more arguments already in stack.
        jmp     .read_vars


;******************************************************************
; reads main source file name.

.read_main_source_name:

        stdcall StrLen, esi
        mov     ebx, eax

        test    ebx, ebx
        jz      .nextchunk

        stdcall CreateFStruct, esi, ftMainSource
        mov     edi, eax

        cmp     [.wparam], 0
        jne     .addmain        ; loaded from template so someone else have to bother for consistency

        stdcall GetModified, edi
        test    eax, eax
        jz      .addmain

        stdcall ProjectInconsistent, edi
        cmp     eax, IDNO
        jne     .addmain

        stdcall FreeFStruct, edi
        mov     [fProjModified], TRUE
        jmp     .nextchunk

.addmain:
        push   [fProjModified]
        invoke SendMessageW, [.hwnd], PMM_ADDFILE, edi, NULL
        pop    [fProjModified]

        jmp     .nextchunk


;******************************************************************

.read_compiler_memory:
        lodsd
        stdcall SetCompilerMemorySize, eax
        jmp     .nextchunk

;******************************************************************

.read_output_filename:
        xor     eax, eax
        lodsb
        mov     [poBinNameAuto], eax

        stdcall StrCopy, [poBinName], esi
        jmp     .nextchunk

;******************************************************************
oncase _CEM_GETTEXT
        mov     eax, [ptrProjectFile]
        stdcall ChangeDir, [eax+TOpenFile.hFileName]

        mov     esi, [.lparam]  ; pointer to TInMemoryFile

        stdcall GetMem, 65536       ; Maximal size of the project file.
        jc      .errorgettext

        mov     [esi+TInMemoryFile.ptrBuffer], eax
        mov     edi, eax

; magic header.
        mov     eax, 'FNPr'
        stosd
        mov     eax, $0a1a0a0d  ;) magic number. 'CR, LF, EOF, LF'
        stosd

;_________________________
;
; write chunk MAIN
;_________________________

        invoke  SendMessageW, [.hwnd], PMM_GETMAINFILE, 0, 0
        test    eax, eax
        jz      .main_chunk_ok

        mov     ecx, eax
        mov     eax, 'MAIN'
        stosd

        add     edi, 4
        mov     esi, edi

        stdcall GetRelativePath, [ecx+TOpenFile.hFileName]
        push    eax
        call    .StrToStream
        stdcall StrDel ; from the stack
        xor     al,al
        stosb   ; end of string.

        call    .finalize_chunk
.main_chunk_ok:

;_________________________
;
; write chunks CTNM
;_________________________
        mov     eax, 'CTNM'     ; compiler target name.
        stosd

        add     edi, 4
        mov     esi, edi

        mov     al, byte [poBinNameAuto]
        stosb

        mov     eax, [poBinName]
        stdcall .StrToStream

        mov     al, 0
        stosb

        call    .finalize_chunk

;_________________________
;
; write chunks CMEM
;_________________________

        mov     eax, 'CMEM'
        stosd

        add     edi, 4
        mov     esi, edi

        mov     eax, [poMemory]
        stosd

        call    .finalize_chunk

;_________________________
;
; write chunks VARS
;_________________________

        mov     ebx, [poVarArray]
        test    ebx, ebx
        jz      .vars_ok

        mov     ecx, [ebx+TArray.count]
        lea     ebx, [ebx+TArray.array]
        jecxz   .vars_ok

        mov     eax, 'VARS'
        stosd

        add     edi, 4
        mov     esi, edi

.varsloop:
        mov     eax, [ebx+TEnvVarType.pName]
        call    .StrToStream
        mov     al, 0
        stosb
        mov     eax, [ebx+TEnvVarType.pValue]
        call    .StrToStream
        mov     al, 0
        stosb

        add     ebx, sizeof.TEnvVarType
        dec     ecx
        jnz     .varsloop

        stosb   ; final zero.

        call    .finalize_chunk

.vars_ok:

;_________________________
;
; write chunks PRTR
;_________________________

; Save categories...
        invoke  GetDlgItem, [.hwnd], ProjTreeCtrlID

        ;  proc CategoriesScan, hTree, tvRoot, lparam, ptrCallback
        stdcall CategoriesScan, eax, TVI_ROOT, eax, SaveOneCategory


; all is ok, so finish this work.
        mov     esi, [.lparam]
        sub     edi, [esi+TInMemoryFile.ptrBuffer]
        mov     [esi+TInMemoryFile.fileSize], edi
        xor     eax, eax
        mov     [esi+TInMemoryFile.fAscii], eax
        clc
        return

.errorgettext:
        mov     eax, 1
        stc
        return

;******************************************************************************************


oncase  _CEM_CHANGENOTIFY
; change directory to the new name...
        mov     ebx, [ptrProjectFile]
        stdcall ChangeDir, [ebx+TOpenFile.hFileName]
        stdcall SetMainTitle
        stdcall AddToMRUList, [ptrMRUProj], [ebx+TOpenFile.hFileName]
        clc
        return


oncase _CEM_FOCUSFILE
        invoke  SetFocus, [.hwnd]
        clc
        return


oncase _CEM_CANACTION
        xor     eax, eax
        clc
        return

oncase _CEM_EDITACTION
        clc
        return


oncase _CEM_GETFILE
        mov     eax, [ptrProjectFile]
        clc
        return


oncase _CEM_GETMODIFIED
        mov     eax, [fProjModified]
        clc
        return


oncase _CEM_SETMODIFIED
        push    [.wparam]
        pop     [fProjModified]
        clc
        return


oncase WM_DROPFILES

        DebugMsg "WM_DROPFILES"

        lea     esi, [.str]
        invoke  DragQueryFileW, [.wparam], -1, esi, 1024
        mov     ebx, eax
        xor     edi, edi

.opendroploop:
        invoke  DragQueryFileW, [.wparam], edi, esi, 1024
        stdcall WideCharToUtf8, esi
        push    eax

        stdcall OpenFileByName, eax, NULL, FALSE
        stdcall StrDel ; from the stack

        test    eax, eax
        jz      .next
        invoke  SendMessageW, [.hwnd], PMM_ADDFILE, eax, -1
.next:
        inc     edi
        dec     ebx
        jnz     .opendroploop

        invoke  DragFinish, [.wparam]

        xor     eax, eax
        clc
        return


oncase WM_COMMAND

        DebugMsg "WM_COMMAND to the project manager!"
        OutputValue "wparam = ", [.wparam], 16, 8
        OutputValue "lparam = ", [.lparam], 16, 8

        movzx   eax, word [.wparam]     ; control ID

        cmp     eax, ProjSettingsID
        je      .projsettings

        cmp     eax, IDOK
        je      .enter_key

        cmp     eax, FirstID
        jb      .from_menu

        stc
        return


.projsettings:
locals
  .btnrect RECT
endl
        stdcall SetCurrentFile, [ptrProjectFile]

        invoke  GetDlgItem, [.hwnd], ProjSettingsID
        lea     ebx, [.btnrect]
        invoke  GetWindowRect, eax, ebx
        invoke  TrackPopupMenu, [hSettingsPopup], TPM_LEFTALIGN or TPM_LEFTBUTTON, \
                [.btnrect.left], [.btnrect.top], 0, [.hwnd], 0

        clc
        return


.enter_key:

; this message comes from the main window and has the focused control handle in [.lparam]

        invoke  GetDlgCtrlID, [.lparam]

        cmp     eax, ProjListCtrlID
        je      .listviewenter
        cmp     eax, ProjMainCtrlID
        jne     .ondefault

.listviewenter:
        mov     [.wparam], eax
        jmp     .itemactivate


.from_menu:
; this is actually fast settings menu.

        cmp     word [.wparam+2], 0
        jne     .ondefault              ; not a menu message.

        movzx   eax, word [.wparam]

        shr     eax, 8
        sub     eax, VarMenuBaseID
        mov     esi, [poVarArray]
        cmp     eax, [esi+TArray.count]
        jae     .endmenu

        lea     esi, [esi+eax*sizeof.TEnvVarType + TArray.array]
; so process the value.
        movzx   ecx, word [.wparam]
        and     ecx, $ff

        stdcall StrPtr, [esi+TEnvVarType.pValue]
        mov     edi, eax
        mov     edx, eax

.loop1:
        cmp     byte [edi], 0
        je      .endmenu

        cmp     byte [edi], '|'
        lea     edi, [edi+1]
        jne     .loop1
        loop    .loop1

        push    edi     ; first separator + 1

.loop2:
        mov     al, [edi]
        lea     edi, [edi+1]
        test    al, al
        jz      .endfound
        cmp     al, '|'
        jne     .loop2

.endfound:
        movzx   ecx, al
        mov     byte [edi-1], 0

        pop     esi
        stdcall StrDup, esi
        mov     ebx, eax

        mov     [edi-1], cl       ; restore the separator.
        sub     edi, 2
        sub     esi, 2

.shift:
        cmp     esi, edx
        jb      .endshift

        mov     al, [esi]
        lea     esi, [esi-1]
        mov     [edi], al
        lea     edi, [edi-1]
        jmp     .shift

.endshift:
        mov     byte [edi], '|'
        mov     edi, edx

        stdcall StrPtr, ebx
        mov     esi, eax

.copyloop:
        mov     al, [esi]
        lea     esi, [esi+1]

        test    al, al
        jz      .endcopy

        mov     [edi], al
        lea     edi, [edi+1]
        jmp     .copyloop

.endcopy:
        stdcall StrDel, ebx
        stdcall CreateParametersMenu
        invoke  SendMessageW, [.hwnd], _CEM_SETMODIFIED, TRUE, 0

.endmenu:
        clc
        return



;**********************************************************************************************

oncase PMM_GETMAINFILE

        mov     [.lvi.mask], LVIF_PARAM
        mov     [.lvi.iItem], 0
        lea     eax, [.lvi]
        invoke  SendDlgItemMessageW, [.hwnd], ProjMainCtrlID, LVM_GETITEMW, 0, eax
        test    eax, eax
        jz      .endgetmain
        mov     eax, [.lvi.lParam]
.endgetmain:
        clc
        return

;**********************************************************************************************

oncase WM_NOTIFY

        mov     ecx, [.wparam]
        mov     esi, [.lparam]

        cmp     [esi+NMHDR.code], NM_SETFOCUS
        je      .set_current_file

        cmp     ecx, ProjTreeCtrlID
        je      .treenotify
        cmp     ecx, ProjMainCtrlID
        je      .listnotify
        cmp     ecx, ProjListCtrlID
        je      .listnotify

        stc
        return

; ---------- NM_SETFOCUS ---------------------------------

.set_current_file:

        stdcall SetCurrentFile, [ptrProjectFile]
        xor     eax, eax
        clc
        return


;-----------ListView notification messages-----------------

.listnotify:
        cmp     [esi+NMHDR.code], LVN_ITEMACTIVATE
        je      .itemactivate
        cmp     [esi+NMHDR.code], LVN_KEYDOWN
        je      .listkey
        cmp     [esi+NMHDR.code], NM_RCLICK
        je      .lvrightclick

        stc
        return


.lvrightclick:
        push    [hListPopup]

        lea     eax, [.pnt]
        invoke  GetCursorPos, eax
        pop     eax

        invoke  TrackPopupMenu, eax, TPM_LEFTALIGN or TPM_RIGHTBUTTON, [.pnt.x], [.pnt.y], 0, [.hwnd], NULL
        jmp     .qfalse

.itemactivate:
        xor     ebx, ebx
        dec     ebx

.openloop:
        invoke  SendDlgItemMessageW, [.hwnd], [.wparam], LVM_GETNEXTITEM, ebx, LVNI_SELECTED
        cmp     eax, -1
        je      .qfalse

        mov     ebx, eax

        mov     [.lvi.mask], LVIF_PARAM
        mov     [.lvi.iItem], eax
        lea     eax, [.lvi]
        invoke  SendDlgItemMessageW, [.hwnd], [.wparam], LVM_GETITEMW, 0, eax
        stdcall LoadFile, [.lvi.lParam], NULL
        jmp     .openloop


;---------- Del key is pressed in the list view -------------------
.listkey:
        cmp     [esi+LVKEYDOWN.wVKey], VK_DELETE
        jne     .qfalse

        stdcall OnDelFile, 0, 0

        xor     eax, eax
        clc
        return

;--- Category tree notification messages ---------
.treenotify:
        cmp     [esi+NMHDR.code], TVN_SELCHANGEDW
        je      .selchanged

        cmp     [esi+NMHDR.code], TVN_KEYDOWN
        je      .keydown

        cmp     [esi+NMHDR.code], NM_RCLICK
        je      .tvrightclick

        cmp     [esi+NMHDR.code], TVN_ENDLABELEDITW
        je      .endlabeledit

        cmp     [esi+NMHDR.code], TVN_DELETEITEMW
        jne     .qfalse

.deleteitem:
        mov     edi, [esi+NMTREEVIEW.itemOld.lParam]
        test    edi, edi
        jz      .qfalse

        test    [esi+NMTREEVIEW.itemOld.state], TVIS_SELECTED or TVIS_FOCUSED
        jz      .freelist

        invoke  SendDlgItemMessageW, [.hwnd], ProjListCtrlID, LVM_DELETEALLITEMS, 0, 0

.freelist:
        stdcall ListFree, edi, FileListFreeCallback
        jmp     .qfalse

;----------- end of label editing ------------------------
.endlabeledit:
        mov     esi, [.lparam]
        cmp     [esi+TVDISPINFO.item.pszText], 0
        je      .endedit

        mov     [fProjModified], TRUE
        mov     [esi+TVDISPINFO.item.mask], TVIF_TEXT
        lea     esi, [esi+TVDISPINFO.item]
        invoke  SendDlgItemMessageW, [.hwnd], ProjTreeCtrlID, TVM_SETITEMW, 0, esi

.endedit:
       clc
       return

;---------- right click on treeview control --------------
.tvrightclick:
        lea     eax, [.pnt]
        invoke  GetCursorPos, eax
        invoke  TrackPopupMenu, [hTreePopup], TPM_LEFTALIGN or TPM_RIGHTBUTTON, [.pnt.x], [.pnt.y], 0, [.hwnd], NULL
        jmp     .qfalse


;---------- selection changed notification ---------------
.selchanged:
; load the new selected category in the listview...
        stdcall ReloadListView, [.hwnd], [esi+NMTREEVIEW.itemNew.hItem]
        xor     eax, eax
        clc
        return

;------------- key down notification -----------------
.keydown:
        cmp     [esi+TVKEYDOWN.wVKey], VK_DELETE
        jne     .qfalse

        stdcall OnDelCategory, 0, 0
        jmp     .qfalse


oncase WM_CLOSE
        stdcall CloseFile, [ptrProjectFile]
        cmp     eax, cfrDestroyed
        jne     .qfalse
        mov     [ptrProjectFile], 0
        jmp     .qfalse


oncase WM_DESTROY
        mov     [ptrProjectFile], 0
        stdcall SetMainTitle

        invoke  SendMessageW, [.hwnd], PMM_GETMAINFILE, 0, 0
        test    eax, eax
        jz      .mainfree

        stdcall FileListFreeCallback, eax

.mainfree:
        stdcall DeleteAllTVItems, [hProjManager] ; clear all items in the project and frees the memory.
        mov     [hProjManager], 0

        stdcall __RefreshLeftPane

        stdcall StrDel, [poBinName]
        mov     [poBinName], 0
        mov     [poBinNameAuto], TRUE

        cmp     [poVarArray], 0
        je      .qfalse

        stdcall FreeMem, [poVarArray]
        mov     [poVarArray], 0

        jmp     .qfalse


cMRUProjSection text 'MRU_projects'

oncase AM_INITWINDOW

        stdcall SetSplitGrid, [.hwnd], ProjLayout
        stdcall AttachToCell, [hProjManager], [hMainForm], MainLayout.cellProjectManager

        stdcall __RefreshLeftPane

        xor     ebx, ebx

        invoke  CreateWindowExW, 0, cButtonClassName, ebx,                  \
                                WS_CHILD or WS_VISIBLE or BS_OWNERDRAW or WS_CLIPSIBLINGS or  \
                                BS_VCENTER or WS_TABSTOP,                   \
                                ebx, ebx, ebx, 16,                          \
                                [.hwnd], ProjSettingsID, [hInstance], ebx
        mov     edi, eax
;        invoke  SendMessageW, edi, BM_SETCHECK, BST_CHECKED, 0
        stdcall AttachToCell, edi, [.hwnd], ProjLayout.cellSettings

        invoke  CreateWindowExW, WS_EX_CLIENTEDGE, cListviewClassName, ebx,      \
                                WS_CHILD or WS_VISIBLE or LVS_REPORT or         \
                                LVS_SHAREIMAGELISTS or LVS_SORTASCENDING or     \
                                LVS_NOSCROLL or LVS_NOCOLUMNHEADER or           \
                                LVS_NOSORTHEADER or WS_TABSTOP or WS_CLIPSIBLINGS,                 \
                                ebx, ebx, ebx, 20,                              \
                                [.hwnd], ProjMainCtrlID, [hInstance], ebx
        mov     edi, eax
        stdcall AttachToCell, edi, [.hwnd], ProjLayout.cellMain

        mov     [.col.mask], LVCF_FMT or LVCF_WIDTH
        mov     [.col.fmt], LVCFMT_LEFT
        mov     [.col.cx], 1024
        lea     eax, [.col]
        invoke  SendMessageW, edi, LVM_INSERTCOLUMNW, ebx, eax
        invoke  SendMessageW, edi, LVM_SETIMAGELIST, LVSIL_SMALL, [hTabIml]

        invoke  CreateWindowExW, WS_EX_CLIENTEDGE,                                                       \
                                cTreeViewClassName, ebx,                                                \
                                WS_CHILD or WS_VISIBLE or TVS_DISABLEDRAGDROP or TVS_HASBUTTONS or WS_CLIPSIBLINGS or    \
                                TVS_SHOWSELALWAYS or TVS_HASLINES or TVS_LINESATROOT or TVS_EDITLABELS or WS_TABSTOP, \
                                ebx, ebx, ebx, 100,                                                     \
                                [.hwnd], ProjTreeCtrlID, [hInstance], ebx
        mov     edi, eax
        invoke  SendMessageW, edi, TVM_SETIMAGELIST, TVSIL_NORMAL, [imlCategories]
        stdcall AttachToCell, edi, [.hwnd], ProjLayout.cellCategories


        invoke  CreateWindowExW, WS_EX_CLIENTEDGE, cListviewClassName, ebx,              \
                                WS_CHILD or WS_VISIBLE or LVS_REPORT or WS_CLIPSIBLINGS or     \
                                LVS_SHAREIMAGELISTS or LVS_SORTASCENDING or            \
                                LVS_NOCOLUMNHEADER or                                  \
                                LVS_SHOWSELALWAYS or WS_TABSTOP or LVS_NOSORTHEADER,  \
                                ebx, ebx, ebx, ebx,                                     \
                                [.hwnd], ProjListCtrlID, [hInstance], ebx

        mov     edi, eax
        stdcall AttachToCell, edi, [.hwnd], ProjLayout.cellFiles

        invoke  SendMessageW, edi, LVM_SETIMAGELIST, LVSIL_SMALL, [hTabIml]
        invoke  SendMessageW, edi, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_LABELTIP

        mov     [.col.mask], LVCF_FMT or LVCF_SUBITEM
        mov     [.col.fmt], LVCFMT_LEFT
        mov     [.col.iSubItem], 0
        lea     eax, [.col]
        invoke  SendMessageW, edi, LVM_INSERTCOLUMNW, ebx, eax

.qfalse:
        xor     eax,eax
        clc
        return


oncase WM_DRAWITEM

        cmp     [.wparam], ProjSettingsID
        jne     .ondefault

        mov     esi, [.lparam]
        mov     edi, [esi+DRAWITEMSTRUCT.hDC]
        lea     ebx, [esi+DRAWITEMSTRUCT.rcItem]

        invoke  SetBkMode, edi, TRANSPARENT

        invoke  GetSysColor, COLOR_BTNFACE
        invoke  CreateSolidBrush, eax
        push    eax
        invoke  FillRect, edi, ebx, eax
        invoke  DeleteObject ; from the stack.

        mov     eax, EDGE_RAISED
        test    [esi+DRAWITEMSTRUCT.itemState], ODS_SELECTED
        jz      .edge_ok

        mov     eax, EDGE_SUNKEN

.edge_ok:
        invoke  DrawEdge, edi, ebx, eax, BF_RECT

        stdcall StrPtr, [hSettingsString]
        test    eax, eax
        je      .setshort
        cmp     eax, -1
        jne     .drawit

.setshort:
        mov     eax, cSettingsCaption

.drawit:
        stdcall utf8ToWideChar, eax
        push    eax

        add     [ebx+RECT.left],4
        invoke  DrawTextW, edi, eax, -1, ebx, DT_LEFT or DT_VCENTER or DT_SINGLELINE
        stdcall FreeMem ; from the stack

        test    [esi+DRAWITEMSTRUCT.itemState], ODS_FOCUS
        jz      .draw_ok

        sub     [ebx+RECT.left], 2
        sub     [ebx+RECT.right], 2
        add     [ebx+RECT.top], 2
        sub     [ebx+RECT.bottom], 2
        invoke  DrawFocusRect, edi, ebx

.draw_ok:

        clc
        mov     eax, 1
        return

        enddispatch
endp




; it should return CF=1 if the result is still not found.
; arguments are [ptrList], [hTVItem] and [.lparam]
proc SaveOneCategory, .ptrList, .hItem, .hTree
.tvi TVITEM
.count dd ?
begin
        push    ebx esi

        mov     eax, 'PRTR'
        stosd

        add     edi, 4
        mov     esi, edi        ; pointer to the data.

        stdcall GetFullItemName, [.hTree], [.hItem]
        mov     ebx, eax

        mov     [.tvi.mask], TVIF_IMAGE or TVIF_PARAM
        mov     eax, [.hItem]
        mov     [.tvi.hItem], eax
        lea     eax, [.tvi]
        invoke  SendMessageW, [.hTree], TVM_GETITEMW, 0, eax
        test    eax, eax
        jz      .finish

        mov     eax, [.tvi.iImage]         ; image index.
        stosd
        xor     eax, eax                   ; reserved.
        stosd

        mov     eax, ebx
        call    ProjManProc.StrToStream
        xor     al,al
        stosb

        stdcall StrDel, ebx     ; category name.

        mov     ebx, [.tvi.lParam]
        mov     edx, [ebx+TArray.count]
        mov     [.count], edx
        lea     ebx, [ebx+TArray.array]

.looplist:
        dec     [.count]
        js      .finish

        mov     eax, [ebx]      ; ptr to TOpenFile
        lea     ebx, [ebx+4]

        stdcall GetRelativePath, [eax+TOpenFile.hFileName]
        push    eax
        call    ProjManProc.StrToStream
        stdcall StrDel ; from the stack
        xor     al,al
        stosb
        jmp     .looplist

.finish:
        xor     eax, eax
        stosb

        call    ProjManProc.finalize_chunk

        stc
        pop     esi ebx
        return
endp



proc FileListFreeCallback, .ptrOpenFile
begin
        push    esi
        mov     esi, [.ptrOpenFile]

        mov     [esi+TOpenFile.projectID], FALSE
        stdcall FileChanged, esi, fcEditor
        pop     esi
        return
endp


;***********************************************************
; Set the filename of the project and updates the title of
; the main window.
;***********************************************************
proc SetMainTitle
begin
        push    esi

        xor     eax, eax
        mov     esi, [ptrProjectFile]
        test    esi, esi
        jz      @f

        mov     eax, [esi+TOpenFile.hFileName]

@@:
        stdcall SetControlTextUtf8, [hMainForm], eax
        pop     esi
        return
endp



;***********************************************************
; List view utils.
;***********************************************************
proc ReloadListView, .hProj, .hCategory
begin
        push    edi ebx

        invoke  SendDlgItemMessageW, [.hProj], ProjListCtrlID, LVM_DELETEALLITEMS, 0, 0
        stdcall GetTVItemPtr, [.hProj], [.hCategory]
        mov     edi, eax

        mov     ebx, [edi+TArray.count]

        OutputValue "Listview count: ", ebx, 10, -1

        invoke  SendDlgItemMessageW, [.hProj], ProjListCtrlID, LVM_SETITEMCOUNT, ebx, 0

        lea     edi, [edi+TArray.array]

.addnewlist:
        dec     ebx
        js      .finish

        stdcall AddToListView, [.hProj], ProjListCtrlID, [edi]
        lea     edi, [edi+4]
        jmp     .addnewlist

.finish:
        invoke  GetDlgItem, [.hProj], ProjListCtrlID
        invoke  PostMessageW, eax, LVM_SETCOLUMNWIDTH, 0, LVSCW_AUTOSIZE
        pop     ebx edi
        return
endp


proc AddToListView, .hProject, .ListID, .ptrOpenFile
.lvi LVITEM
begin
        push    esi
        mov     esi, [.ptrOpenFile]

        stdcall GetScreenProperties, esi

;        OutputValue "Screen name: ", eax, 16, 8

        test    eax, eax
        jz      .finish

        push    eax     ; for StrDel
        stdcall utf8ToWideChar, eax
        stdcall StrDel ; from the stack

        mov     [.lvi.mask], LVIF_IMAGE or LVIF_PARAM or LVIF_TEXT
        mov     [.lvi.iItem], 0
        mov     [.lvi.iSubItem], 0
        mov     [.lvi.iImage], edx
        mov     [.lvi.lParam], esi
        mov     [.lvi.pszText], eax

        lea     eax, [.lvi]
        invoke  SendDlgItemMessageW, [.hProject], [.ListID], LVM_INSERTITEMW, 0, eax
        stdcall FreeMem, [.lvi.pszText]

.finish:
        pop     esi
        return
endp


;------------------------------------------------------------;
; Searches the project for the file and returns:             ;
; eax - handle of the category that contain this file.       ;
;   eax = 0 if the file is main file.                        ;
;   eax = -1 if the file was not found.                      ;
; edx - index in TList containing the file pointer.          ;
;------------------------------------------------------------;
proc SearchForFile, .hProject, .ptrOpenFile
begin
        invoke  SendMessageW, [.hProject], PMM_GETMAINFILE, 0, 0
        test    eax, eax
        jz      .nomain

        cmp     eax, [.ptrOpenFile]
        je      .main

.nomain:
        invoke  GetDlgItem, [.hProject], ProjTreeCtrlID
        mov     ebx, eax
        stdcall CategoriesScan, ebx, TVI_ROOT, [.ptrOpenFile], _SearchCategory
        return

.main:
        mov     edx, eax
        xor     eax, eax
        return
endp


proc _SearchCategory, .ptrList, .hTVItem, .ptrOpenFile
begin
        stdcall ListIndexOf, [.ptrList], [.ptrOpenFile]
        jc      .finish

        mov     edx, [.hTVItem]
        xchg    eax, edx
        clc
.finish:
        return
endp


;---------------------------------------------------------------
; Recursively loops through all categories and calls callback
; procedure for every category.
;---------------------------------------------------------------
proc CategoriesScan, .hTree, .tvRoot, .lparam, .ptrCallback
.tvi TVITEM
begin
        push    esi ebx
        invoke  SendMessageW, [.hTree], TVM_GETNEXTITEM, TVGN_CHILD, [.tvRoot]

.loop:
        mov     ebx, eax
        test    ebx, ebx
        jz      .notfound

        mov     [.tvi.mask], TVIF_PARAM
        mov     [.tvi.hItem], ebx
        lea     eax, [.tvi]
        invoke  SendMessageW, [.hTree], TVM_GETITEMW, 0, eax

        mov     esi, [.tvi.lParam]
        test    esi, esi
        jz      .listdone

; it should return CF=1 if the result is still not found.
; arguments are [ptrList] and [hTVItem] and [.lparam]
        stdcall [.ptrCallback], esi, ebx, [.lparam]
        jnc     .found

.listdone:
        stdcall CategoriesScan, [.hTree], ebx, [.lparam], [.ptrCallback]
        jnc     .found

        invoke  SendMessageW, [.hTree], TVM_GETNEXTITEM, TVGN_NEXT, ebx
        jmp     .loop

.notfound:
        stc
        pop     ebx esi
        return

.found:
        pop     ebx esi
        return
endp


;---------------------------------------------------------------
; Searches the category item with given path.
;---------------------------------------------------------------
proc CategorySearch, .hTree, .path
.str dd ?
.tvi TVITEM
.buffer rw 512
.root dd ?
begin
        pushad

        stdcall StrDupMem, [.path]
        mov     [.str], eax
        stdcall StrPtr, eax
        mov     esi, eax

        mov     edi, TVI_ROOT

.search_name:
        mov     ebx, esi

.sub_loop:
        lodsb
        test    al, al
        jz      .found_dir

        cmp     al, '\'
        je      .found_dir

        cmp     al, '/'
        jne     .sub_loop

.found_dir:
        mov     byte [esi-1], 0
        test    al, al
        jnz     .subdir_ok
        dec     esi

.subdir_ok:
        invoke  SendMessageW, [.hTree], TVM_GETNEXTITEM, TVGN_CHILD, edi

.inner_loop:
        mov     edi, eax
        test    edi, edi
        jnz     .compare

        stc
        jmp     .finish

.compare:
        mov     [.tvi.mask], TVIF_PARAM or TVIF_TEXT
        mov     [.tvi.hItem], edi
        lea     eax, [.buffer]
        mov     [.tvi.pszText], eax
        mov     [.tvi.cchTextMax], 512

        lea     eax, [.tvi]
        invoke  SendMessageW, [.hTree], TVM_GETITEMW, 0, eax
        test    eax, eax
        jz      .next

        stdcall WideCharToUtf8, [.tvi.pszText]
        push    eax

        stdcall StrCompNoCase, eax, ebx
        stdcall StrDel ; from the stack

        jc      .found_name

.next:
        invoke  SendMessageW, [.hTree], TVM_GETNEXTITEM, TVGN_NEXT, edi
        jmp     .inner_loop

.found_name:
        cmp     byte [esi], 0
        jne     .search_name

        mov     [esp+regEAX*4], edi
        clc

.finish:
        stdcall StrDel, [.str]
        popad
        return
endp



;---------------------------------------------
; Returns handle to string contains full path
; name for given tree-view item.
; returns also, the image index of the item in ecx
;---------------------------------------------
proc GetFullItemName, .hTree, .hItem
.tvi TVITEM
.str rb 512
.img dd ?
begin
        push    esi ebx

        stdcall StrNew
        mov     esi, eax
        mov     eax, [.hItem]

.buildloop:
        mov     ebx, eax
        test    ebx, ebx
        jz      .rootoftree

        mov     [.tvi.mask], TVIF_TEXT or TVIF_IMAGE
        mov     [.tvi.hItem], ebx
        lea     eax, [.str]
        mov     [.tvi.pszText], eax
        mov     [.tvi.cchTextMax], 256
        lea     eax, [.tvi]
        invoke  SendMessageW, [.hTree], TVM_GETITEMW, 0, eax

        cmp     ebx, [.hItem]
        jne     .dostring

        push    [.tvi.iImage]
        pop     [.img]

.dostring:
        stdcall WideCharToUtf8, [.tvi.pszText]
        stdcall StrCharCat, eax, '\'

        stdcall StrInsert, esi, eax, 0
        stdcall StrDel, eax

        invoke  SendMessageW, [.hTree], TVM_GETNEXTITEM, TVGN_PARENT, ebx
        jmp     .buildloop

.rootoftree:
        mov     eax, esi
        mov     ecx, [.img]
        pop     ebx esi
        return
endp


;***********************************************************
; TreeView utils. This is small library for easy work with
; TreeView control.
;***********************************************************

;**************************************************************
; Returns lParam of the TreeView item.
;**************************************************************
proc GetTVItemPtr, .hProject, .hItem
.item   TVITEM
begin
        push    ecx edx

        push    [.hItem]
        pop     [.item.hItem]
        mov     [.item.mask], TVIF_PARAM
        lea     eax, [.item]
        invoke  SendDlgItemMessageW, [.hProject], ProjTreeCtrlID, TVM_GETITEMW, 0, eax
        test    eax, eax
        jz      .error

        mov     eax, [.item.lParam]
        clc
        pop     edx ecx
        return

.error:
        stc
        pop     edx ecx
        return
endp



;**************************************************************
; Sets lParam of the TreeView item.
;**************************************************************
proc SetTVItemPtr, .hProject, .hItem, .lparam
.item   TVITEM
begin
        push    eax ecx edx
        push    [.hItem]
        pop     [.item.hItem]
        mov     [.item.mask], TVIF_PARAM
        push    [.lparam]
        pop     [.item.lParam]
        lea     eax, [.item]
        invoke  SendDlgItemMessageW, [.hProject], ProjTreeCtrlID, TVM_SETITEMW, 0, eax
        pop     edx ecx eax
        return
endp





;***********************************************
; Handler of action actMoveCategory
;***********************************************
proc OnMoveCategory, .wparam, .lparam
begin
        xor     ebx, ebx
        cmp     [.wparam], actMoveCategoryUp
        je      @f
        inc     ebx
@@:
        invoke  GetDlgItem, [hProjManager], ProjTreeCtrlID
        mov     esi, eax

        invoke  SendMessageW, esi, TVM_GETNEXTITEM, TVGN_CARET, 0
        stdcall MoveTVItem, esi, eax, ebx
        mov     [fProjModified], TRUE
        return
endp


;****************************************************************
; Moves one hItem up or down in the treeview, depending of the
; fMoveDown:
;    FALSE - move up
;    TRUE  - move down
;****************************************************************
proc MoveTVItem, .hTreeView, .hItem, .fMoveDown
.ptr dd ?
.obj dd ?
.tvi TVITEM
.lparam dd ?
.tvs TVSORTCB
begin
        push    esi edi ebx

        invoke  SendMessageW, [.hTreeView], TVM_GETCOUNT, 0, 0
        shl     eax, 2
        stdcall GetMem, eax
        mov     [.ptr], eax
        mov     edi, eax

        mov     eax, [.hItem]
        mov     [.tvi.hItem], eax
        mov     [.tvi.mask], TVIF_PARAM
        lea     eax, [.tvi]
        invoke  SendMessageW, [.hTreeView], TVM_GETITEMW, 0, eax
        mov     eax, [.tvi.lParam]
        mov     [.lparam], eax

        invoke  SendMessageW, [.hTreeView], TVM_GETNEXTITEM, TVGN_PARENT, [.hItem]
        mov     ebx, eax

        invoke  SendMessageW, [.hTreeView], TVM_GETNEXTITEM, TVGN_CHILD, ebx
.getloop:
        test    eax, eax
        jz      .endofchildren

        mov     [.tvi.hItem], eax
        mov     [.tvi.mask], TVIF_PARAM or TVIF_HANDLE
        lea     eax, [.tvi]
        invoke  SendMessageW, [.hTreeView], TVM_GETITEMW, 0, eax
        mov     eax, [.tvi.lParam]
        cmp     eax, [.lparam]
        jne     .storeit
        mov     [.obj], edi
.storeit:
        stosd

        invoke  SendMessageW, [.hTreeView], TVM_GETNEXTITEM, TVGN_NEXT, [.tvi.hItem]
        jmp     .getloop

.endofchildren:
        stosd
        cmp     [.fMoveDown], FALSE
        je      .moveup

.movedown:
        mov     edi, [.obj]
        mov     eax, [edi+4]
        test    eax, eax
        jz      .cantmove       ; it is the last element.

        mov     ecx, [edi]
        mov     [edi], eax
        mov     [edi+4], ecx
        jmp     .sortit

.moveup:
        mov     edi, [.obj]
        cmp     edi, [.ptr]
        je      .cantmove               ; it is the first element

        mov     eax, [edi-4]
        mov     ecx, [edi]
        mov     [edi], eax
        mov     [edi-4], ecx

.sortit:
        mov     [.tvs.hParent], ebx
        mov     [.tvs.lpfnCompare], CompareTwoItems
        mov     eax, [.ptr]
        mov     [.tvs.lParam], eax
        lea     eax, [.tvs]
        invoke  SendMessageW, [.hTreeView], TVM_SORTCHILDRENCB, 0, eax

.cantmove:
        stdcall FreeMem, [.ptr]
        pop     ebx edi esi
        return
endp


proc CompareTwoItems, .lParam1, .lParam2, .ptrList
begin
        push    esi

        mov     esi, [.ptrList]
        mov     ecx, [.lParam1]
        mov     edx, [.lParam2]
.loop:
        lodsd
        test    eax, eax
        jz      .notfound

        cmp     eax, ecx
        je      .first

        cmp     eax, edx
        jne     .loop

.second:
        xor     eax, eax
        inc     eax
        pop     esi
        return

.first:
        xor     eax, eax
        dec     eax
        pop     esi
        return

.notfound:
        xor     eax, eax
        pop     esi
        return
endp


;****************************************************************
; Returns in ebx the handle of parent treeview item.
;****************************************************************
proc GetItemParent, .hItem
begin
        push    eax ecx edx

        invoke  SendDlgItemMessageA, [hProjManager], ProjTreeCtrlID, TVM_GETNEXTITEM, TVGN_PARENT, [.hItem]
        test    eax,eax
        jz      .error
        clc
        jmp     .finish

.error:
        stc
.finish:
        mov     ebx, eax
        pop     edx ecx eax
        return
endp


proc DeleteAllTVItems, .hProj
begin
.loop:
        invoke  SendDlgItemMessageW, [.hProj], ProjTreeCtrlID, TVM_GETNEXTITEM, TVGN_ROOT, 0
        test    eax, eax
        jz      .finish

        stdcall DeleteOneTVItem, [.hProj], eax
        jmp     .loop
.finish:
        return
endp


proc DeleteOneTVItem, .hProj, .hItem
begin
.clearloop:
        invoke  SendDlgItemMessageW, [.hProj], ProjTreeCtrlID, TVM_GETNEXTITEM, TVGN_CHILD, [.hItem]
        test    eax, eax
        jz      .childrendeleted

        stdcall DeleteOneTVItem, [.hProj], eax
        jmp     .clearloop

.childrendeleted:
        invoke  SendDlgItemMessageW, [.hProj], ProjTreeCtrlID, TVM_DELETEITEM, 0, [.hItem]
        return
endp



;-------------------------------------------------------------------------
; Returns the handle of treeview item that follow the tree of direcories
; of [hFileName]. If there is no some (or any) of the directories of the
; path, it will be created.
; The function checks for "/" and "\" as items separator.
; Arguments:
; hFileName - handle or pointer to the path.
; hRoot     - is root TVItem for category. For examples if category
;             have to be created in the root of the TreeView, hParent
;             should be TVI_ROOT
; Returns:
;   eax - Handle to treeview item that should be parent of the file.
;   edx - pointer to the name of the file without path.
;-------------------------------------------------------------------------
proc GetCategoryTree, .hFileName, .hRoot, .iImage
begin
        push    esi edi ebx



        stdcall StrPtr, [.hFileName]
        mov     esi, eax

        stdcall StrLen, [.hFileName]
        mov     ecx, eax

; find the filename:
.findloop:
        cmp     byte [esi+ecx], '/'
        je      .foundname
        cmp     byte [esi+ecx], '\'
        je      .foundname
        dec     ecx
        jns     .findloop

; Returns the [hRoot] if there is no directories names.
        mov     eax, [.hRoot]
        mov     edx, esi
        pop     ebx edi esi
        return

.foundname:
        lea     edi, [esi+ecx+1]
        pushd   [edi]
        and     dword [edi], 0
        stdcall _DoCategoryTree, esi, [.hRoot], [.iImage]    ; track/create path items.
        popd    [edi]
        mov     edx, edi
        pop     ebx edi esi
        return
endp


proc _DoCategoryTree, .ptrName, .hTVRoot, .iImage
begin
        push    esi edi ebx
        mov     esi, [.ptrName]
        mov     ebx, [.hTVRoot]

.outloop:
        cmp     byte [esi], 0   ; is it the end?
        je      .endloop
        mov     edi, esi
.loop:
        lodsb
        cmp     al,'\'
        je      .foundslash
        cmp     al,'/'
        jne     .loop

.foundslash:
        mov     byte [esi-1], 0
        stdcall _DoGetOneItem, edi, ebx, [.iImage]
        mov     ebx, eax
        mov     byte [esi-1], '/'
        jmp     .outloop

.endloop:
        mov     eax, ebx
        pop     ebx edi esi
        return
endp


proc _DoGetOneItem, .ptrItem, .hParent, .iImage
.tvi TVITEM
.tvins TVINSERTSTRUCT
.str rw 1024
begin
        push    esi edi ebx

        invoke  SendDlgItemMessageW, [hProjManager], ProjTreeCtrlID, TVM_GETNEXTITEM, TVGN_CHILD, [.hParent]
        test    eax, eax
        jz      .notfound

.itemscan:
        mov     ebx, eax
        mov     [.tvi.hItem], eax
        lea     esi, [.str]
        mov     [.tvi.pszText], esi
        mov     [.tvi.cchTextMax], 1024
        mov     [.tvi.mask], TVIF_HANDLE or TVIF_TEXT
        lea     ecx, [.tvi]
        invoke  SendDlgItemMessageW, [hProjManager], ProjTreeCtrlID, TVM_GETITEMW, 0, ecx

        stdcall WideCharToUtf8, esi
        push    eax

        stdcall StrCompNoCase, eax, [.ptrItem]
        stdcall StrDel ; from the stack
        jc      .found

        invoke  SendDlgItemMessageW, [hProjManager], ProjTreeCtrlID, TVM_GETNEXTITEM, TVGN_NEXT, ebx
        test    eax, eax
        jnz     .itemscan

.notfound:
        mov     eax, [.hParent]
        mov     [.tvins.hParent], eax
        mov     [.tvins.hInsertAfter], TVI_LAST
        mov     [.tvins.item.mask], TVIF_TEXT or TVIF_IMAGE or TVIF_SELECTEDIMAGE or TVIF_PARAM
        mov     eax, [.iImage]
        mov     [.tvins.item.iImage], eax
        mov     [.tvins.item.iSelectedImage], eax

        stdcall utf8ToWideChar, [.ptrItem]
        mov     [.tvins.item.pszText], eax

        stdcall CreateArray, 4
        mov     [.tvins.item.lParam], eax

        lea     eax, [.tvins]
        invoke  SendDlgItemMessageW, [hProjManager], ProjTreeCtrlID, TVM_INSERTITEMW, 0, eax
        stdcall FreeMem, [.tvins.item.pszText]

        pop     ebx edi esi
        return

.found:
        mov     eax, ebx
        pop     ebx edi esi
        return
endp



;--------------------------------------------------------------------
; Recursively scans given source file [hFileName] for "include"
; statements and creates treeview items with filenames.
;--------------------------------------------------------------------
proc ScanFileForIncludes, .hFileName, .hTVParent
.tvi TVINSERTSTRUCT
.hthis dd ?
.ptr   dd ?
.cnt   dd ?
begin
        push    esi edi ebx

; Scan the file for includes.
        stdcall LoadBinaryFile, [.hFileName]
        test    eax, eax
        jz      .quit

        mov     [.ptr], eax
        mov     [.cnt], ecx
        test    ecx, ecx
        jz      .finish

; Add the file to the project tree.
        mov     eax, [.hTVParent]
        mov     [.tvi.hParent], eax
        mov     [.tvi.hInsertAfter], TVI_SORT

        stdcall DetectFileType, [.hFileName]
        test    eax, eax
        jz      .finish

        mov     ecx, [eax+TFileType.iFileIcon]
        mov     [.tvi.item.iImage], ecx
        mov     [.tvi.item.iSelectedImage], ecx

        stdcall CreateFStruct, [.hFileName], eax
        mov     [.tvi.item.lParam], eax
        mov     [.tvi.item.mask], TVIF_TEXT or TVIF_IMAGE or TVIF_PARAM or TVIF_SELECTEDIMAGE

        stdcall GetRelativePath, [.hFileName]
        push    eax
        stdcall StrPtr, eax
        mov     [.tvi.item.pszText], eax

        lea     eax, [.tvi]
        invoke  SendDlgItemMessageA, [hProjManager], ProjTreeCtrlID, TVM_INSERTITEM, 0, eax
        mov     [.hthis], eax

        stdcall StrDel ; from the stack

        mov     ecx, [.tvi.item.lParam]
        mov     [ecx+TOpenFile.projectID], eax

        mov     esi, [.ptr]
        mov     ecx, [.cnt]

.scan:
        mov     eax, [esi]
        cmp     al, ';'
        je      .comment
        or      eax, $20202020
        cmp     eax, 'incl'
        je      .maybe
.nextscan:
        inc     esi
        loop    .scan
.finish:
        stdcall FreeMem, [.ptr]
.quit:
        pop     ebx edi esi
        return

.comment:
        cmp     byte [esi], $0d
        je      .scan
        cmp     byte [esi], $0a
        je      .scan
        inc     esi
        loop    .comment
        jmp     .finish

.maybe:
        add     esi, 4
        sub     ecx, 4
        jbe     .finish

        mov     eax, [esi]
        and     eax, $ffffff
        or      eax, $202020
        cmp     eax, 'ude'
        je      .found
        add     esi, 3
        sub     ecx, 3
        jbe     .finish
        jmp     .scan

; Include statement
.found:
        add     esi, 3
        sub     ecx, 3
        jbe     .finish

.foundloop:
        mov     al, [esi]
        cmp     al, '"'
        je      .begin
        cmp     al, "'"
        je      .begin

        cmp     al, ' '
        je      .next
        cmp     al, $9
        jne     .nextscan

.next:
        inc     esi
        loop    .foundloop
        jmp     .finish

.begin:
        inc     esi
        dec     ecx
        jz      .finish
        mov     edi, esi
.scanend:
        cmp     [esi], al
        je      .nameok
        inc     esi
        loop    .scanend
        jmp     .finish
.nameok:
        push    ecx
        mov     byte [esi], 0
;        stdcall ConvertPath, edi
;        push    eax
;        stdcall StrPtr, eax
        stdcall ScanFileForIncludes, edi, [.hthis]
;        stdcall StrDel ; from the stack
        pop     ecx
        inc     esi
        dec     ecx
        jnz     .scan
        jmp     .finish
endp




proc EmptyVarList, .ptrVarArray
begin
        push    esi ebx

        mov     esi, [.ptrVarArray]
        test    esi, esi
        jz      .exit

        cmp     dword [esi], 0
        je      .array_clear

; now delete all strings in the array.
        mov     ebx, [esi]
        mov     ecx, [ebx+TArray.count]
        lea     ebx, [ebx+TArray.array]
        jecxz   .delok

.delloop:
        stdcall StrDel, [ebx+TEnvVarType.pName]
        stdcall StrDel, [ebx+TEnvVarType.pValue]

        add     ebx, sizeof.TEnvVarType
        loop    .delloop
.delok:
; so free the array itself.

        stdcall FreeMem, [esi]

.array_clear:
        stdcall CreateArray, sizeof.TEnvVarType
        mov     [esi], eax

.exit:
        pop     ebx esi
        return
endp






proc _AddKeyValueToArray, .ptrVarArray, .hValue, .hKey
begin
        push    esi

        mov     esi, [.ptrVarArray]
        mov     esi, [esi]
        mov     ecx, [esi+TArray.count]
        lea     esi, [esi+TArray.array-sizeof.TEnvVarType]
        inc     ecx

.searchloop:
        lea     esi, [esi+sizeof.TEnvVarType]
        dec     ecx
        jz      .notfound

        stdcall StrCompNoCase, [esi+TEnvVarType.pName], [.hKey]
        jnc     .searchloop

; found - append it.
        stdcall StrCharCat, [esi+TEnvVarType.pValue], '|'
        stdcall StrCat, [esi+TEnvVarType.pValue], [.hValue]
        stdcall StrDel, [.hValue]

        pop     esi
        return

.notfound:
        mov     ecx, [.ptrVarArray]
        stdcall AddArrayItems, [ecx], 1
        mov     [ecx], edx

        mov     ecx, [.hValue]
        mov     edx, [.hKey]
        mov     [eax+TEnvVarType.pValue], ecx
        mov     [eax+TEnvVarType.pName], edx

        pop     esi
        return
endp



;********************************************************************************************************
; searches the project variables for  given name and copies the value
; (active one) in the buffer [.dest]
; Returns:
;   eax - count of copied chars.
;   CF = 1 - found and copied.
;   CF = 0 - not found.
;********************************************************************************************************

proc SearchForProjectVariable, .hName, .dest
begin
        push    esi edi ecx

        mov     esi, [poVarArray]
        test    esi, esi
        jz      .notfound

        mov     ecx, [esi+TArray.count]
        lea     esi, [esi+TArray.array]

        jecxz   .notfound
.loop:
        stdcall StrCompNoCase, [esi+TEnvVarType.pName], [.hName]
        jc      .found
        lea     esi, [esi+sizeof.TEnvVarType]
        loop    .loop

.notfound:
        clc
        xor     eax, eax
        pop     ecx edi esi
        return

.found:
        mov     edi, [.dest]
        stdcall StrPtr, [esi+TEnvVarType.pValue]
        mov     esi, eax
        xor     eax, eax

.copy:
        mov     cl, [esi]
        lea     esi, [esi+1]

        cmp     cl, 0
        je      .endofcopy

        cmp     cl, '|'
        je      .endofcopy

        mov     [edi], cl
        inc     eax
        lea     edi, [edi+1]
        jmp     .copy

.endofcopy:
        stc
        pop     ecx edi esi
        return
endp




proc SetCompilerMemorySize, .sizeKB
begin
        pushad

        mov     ecx, [.sizeKB]
        mov     eax, cMinimalCompilerMemory
        cmp     ecx, eax
        cmovb   ecx, eax        ; ecx == the new size of the memory block.

        mov     esi, [ptrMemoryAllocated]
        test    esi, esi
        jz      .allocate_new

        cmp     ecx, [poMemory]
        je      .finish_ok

        stdcall FreeMem, esi
        and     [ptrMemoryAllocated], 0 ; in case of out-of-memory error on the new allocation;

.allocate_new:
        mov     [poMemory], ecx
        jecxz   .finish_ok      ; don't allocate memory if the size is 0

        shl     ecx, 10         ; KB -> bytes
        stdcall GetMem, ecx
        jc      .finish                 ; CF == 1; Out of memory. In this case, [ptrMemoryAllocated] == 0

        mov     [ptrMemoryAllocated], eax

.finish_ok:

        clc

.finish:
        popad
        return
endp