Latest source and binaries are attached.
Support for PB-like templates:
In modToolbar.inc change
Toolbar_AddButton hToolBar, i, IDM_FILENEW, 0, TBSTYLE_DROPDOWN
to add a dropdown arrow to the New File item.
In frmMain_OnNotify add the following code to process the dropdown notification:
SELECT CASE pNMHDR->idFrom
CASE IDC_FRMMAIN_TOOLBAR
DIM ptbn AS TBNOTIFY PTR = CAST(TBNOTIFY PTR, pNMHDR)
IF ptbn->hdr.code = TBN_DROPDOWN THEN
SELECT CASE ptbn->iItem
' // Dropdown new file menu
CASE IDM_FILENEW
DIM rc AS RECT
SendMessageW(ptbn->hdr.hwndFrom, TB_GETRECT, cast(WPARAM, IDC_FRMMAIN_TOOLBAR), CAST(LPARAM, @rc))
MapWindowPoints(ptbn->hdr.hwndFrom, HWND_DESKTOP, CAST(POINT PTR, @rc), 2)
frmTemplates_Show(hwnd, rc.Left, rc.Bottom)
END SELECT
END IF
END SELECT
Module for templates loading:
CONST IDC_TEMPLATES_LISTBOX = 101
' ========================================================================================
' Templates popup dialog
' ========================================================================================
FUNCTION frmTemplates_Show (BYVAL hParent AS HWND, BYVAL x AS LONG, BYVAL y AS LONG) AS LONG
DIM pWindow AS CWindow
pWindow.DPI = AfxCWindowPtr(hParent)->DPI
pWindow.Create(hParent, "Templates", @frmTemplates_WndProc, x + 5, y + 5, 432, 422, _
WS_VISIBLE OR WS_CAPTION OR WS_POPUPWINDOW OR WS_THICKFRAME, WS_EX_WINDOWEDGE)
' // Add a listbox
DIM hListBox AS HWND = pWindow.AddControl("ListBox", , IDC_TEMPLATES_LISTBOX, "", _
WS_CHILD OR WS_VISIBLE OR WS_HSCROLL OR WS_VSCROLL OR WS_BORDER OR WS_TABSTOP OR _
LBS_STANDARD OR LBS_HASSTRINGS OR LBS_SORT OR LBS_NOTIFY OR LBS_NOINTEGRALHEIGHT, WS_EX_CLIENTEDGE)
pWindow.SetWindowPos hListBox, NULL, 8, 8, 400, 337, SWP_NOZORDER
SendMessageW hListBox, LB_SETHORIZONTALEXTENT, cast(WPARAM, 600 * pWindow.rxRatio), 0
' // Add the buttons
pWindow.AddControl("Button", , IDOK, "&Ok", 245, 353, 75, 23, WS_CHILD OR WS_VISIBLE OR WS_TABSTOP OR BS_FLAT OR BS_DEFPUSHBUTTON, WS_EX_NOPARENTNOTIFY)
pWindow.AddControl("Button", , IDCANCEL, "&Cancel", 333, 353, 75, 23, WS_CHILD OR WS_VISIBLE OR WS_TABSTOP OR BS_FLAT, WS_EX_NOPARENTNOTIFY)
' // Search templates
DIM hSearch as HANDLE, WFD AS WIN32_FIND_DATAW, FileNo AS LONG, nType AS LONG, nItem AS LONG
DIM wszPath AS WSTRING * MAX_PATH, wszCurPath AS WSTRING * MAX_PATH, wszText AS WSTRING * 260
DIM wszFullPath AS WSTRING * MAX_PATH * 2, idx AS LONG, nLin AS LONG
wszPath = ExePath & "\Templates\"
wszCurPath = wszPath & "*.fbtpl"
' // Count the number of files and dimension the array
' // REDIM PRESERVE causes problems
DIM nCount AS LONG
hSearch = FindFirstFileW(wszCurPath, @WFD)
IF hSearch <> INVALID_HANDLE_VALUE THEN
DO
IF (WFD.dwFileAttributes AND FILE_ATTRIBUTE_DIRECTORY) <> FILE_ATTRIBUTE_DIRECTORY THEN
nCount +=1
END IF
LOOP WHILE FindNextFileW(hSearch, @WFD)
FindClose(hSearch)
END IF
' // Dimension the array
IF nCount = 0 THEN EXIT FUNCTION
DIM rgwszPaths(nCount - 1) AS WSTRING * MAX_PATH
idx = 0
' // Find the files
hSearch = FindFirstFileW(wszCurPath, @WFD)
IF hSearch <> INVALID_HANDLE_VALUE THEN
DO
IF (WFD.dwFileAttributes AND FILE_ATTRIBUTE_DIRECTORY) <> FILE_ATTRIBUTE_DIRECTORY THEN
wszFullPath = wszPath & WFD.cFileName
' // Get the description
FileNo = FREEFILE
OPEN wszFullPath FOR INPUT AS #FileNo
IF ERR = 0 THEN
nLin = 0
DO UNTIL EOF(FileNo)
LINE INPUT #FileNo, wszText
nLin += 1
IF nLin = 1 THEN nType = VAL(wszText)
IF nType < 1 OR nType > 2 THEN EXIT DO
IF nType = 1 AND nLin = 3 THEN EXIT DO
IF nType = 2 AND nLin = 4 THEN EXIT DO
LOOP
CLOSE FileNo
' // Display the description in the listbox
nItem = SendMessageW(hListBox, LB_ADDSTRING, 0, cast(LPARAM, @wszText))
' // Store the full path in the array
rgwszPaths(idx) = wszFullPath
' // Store a pointer to the element of the array in the listbox item
SendMessageW hListBox, LB_SETITEMDATA, nItem, cast(LPARAM, VARPTR(rgwszPaths(idx)))
idx += 1
IF idx > UBOUND(rgwszPaths) THEN EXIT DO
END IF
END IF
LOOP WHILE FindNextFileW(hSearch, @WFD)
FindClose(hSearch)
END IF
' / Process Windows messages
FUNCTION = pWindow.DoEvents
END FUNCTION
' ========================================================================================
' ========================================================================================
' Templates window procedure
' ========================================================================================
FUNCTION frmTemplates_WndProc (BYVAL hwnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT
SELECT CASE uMsg
CASE WM_CREATE
' Disable parent window to make popup window modal
EnableWindow GetParent(hwnd), FALSE
EXIT FUNCTION
CASE WM_COMMAND
SELECT CASE LOWORD(wParam)
' // If ESC key pressed, close the application sending an WM_CLOSE message
CASE IDCANCEL
IF HIWORD(wParam) = BN_CLICKED THEN
SendMessageW hwnd, WM_CLOSE, 0, 0
EXIT FUNCTION
END IF
CASE IDOK
IF HIWORD(wParam) = BN_CLICKED THEN
' // Let the IDC_TEMPLATES_LISTBOX message to process it
SendMessageW hwnd, WM_COMMAND, MAKELONG(IDC_TEMPLATES_LISTBOX, LBN_DBLCLK), 0
EXIT FUNCTION
END IF
CASE IDC_TEMPLATES_LISTBOX
SELECT CASE HIWORD(wParam)
CASE LBN_DBLCLK
' // Get the handle of the Listbox
DIM hListBox AS HWND = GetDlgItem(hwnd, IDC_TEMPLATES_LISTBOX)
' // Get the current selection
DIM curSel AS LONG = SendMessageW(hListBox, LB_GETCURSEL, 0, 0)
IF curSel = LB_ERR THEN EXIT FUNCTION
' // Get the stored index
DIM pwszPath AS WSTRING PTR = cast(WSTRING PTR, SendMessageW(hListBox, LB_GETITEMDATA, cast(WPARAM, curSel), 0))
IF pwszPath = LB_ERR THEN EXIT FUNCTION
' // Open the template
IF pwszPath THEN
' // TODO: Create a new Scintilla control
' // TODO: Load the file
' // TODO: Search for "|", replace it with "" and position the caret in that place
' // Replace the marker with an empty space
' SCIP_FindReplace(pSci, "|", "")
' // Set the state of the document as unmodified
' SCIP_SetSavePoint(pSci)
' // The SCIP_xxx functions are located in the SciCtrl.inc file of the PowerBASIC headers
END IF
' // Close the dialog
SendMessageW hwnd, WM_CLOSE, 0, 0
EXIT FUNCTION
END SELECT
END SELECT
CASE WM_CLOSE
' // Enables parent window keeping parent's zorder
EnableWindow GetParent(hwnd), CTRUE
' // Don't exit; let DefWindowProcW perform the default action
CASE WM_DESTROY
' // End the application by sending an WM_QUIT message
PostQuitMessage(0)
EXIT FUNCTION
END SELECT
' // Default processing of Windows messages
FUNCTION = DefWindowProcW(hWnd, uMsg, wParam, lParam)
END FUNCTION
' ========================================================================================
The attached file contains many templates.
This is the TODO part:
' // Open the template
IF pwszPath THEN
' // TODO: Create a new Scintilla control
' // TODO: Load the file
' // TODO: Search for "|", replace it with "" and position the caret in that place
' // Replace the marker with an empty space
' SCIP_FindReplace(pSci, "|", "")
' // Set the state of the document as unmodified
' SCIP_SetSavePoint(pSci)
' // The SCIP_xxx functions are located in the SciCtrl.inc file of the PowerBASIC headers
END IF
This is how will look, more or less.
Note: Load the template as Untitled<number>.bas.
Thanks Jose :) I have most of the code implemented now. Only had to do some changes in order to make it fit within the style I'm using (message crackers, etc). I also had to make the following small change. You used the toolbar identifier instead of the toolbar button identifier.
Instead of:
SendMessageW(ptbn->hdr.hwndFrom, TB_GETRECT, Cast(WPARAM, IDC_FRMMAIN_TOOLBAR), Cast(LPARAM, @rc))
Use:
SendMessageW(ptbn->hdr.hwndFrom, TB_GETRECT, IDM_FILENEW, Cast(LPARAM, @rc))
I also removed the WS_THICKFRAME style from the Templates window.
I coded it as a separate test without a toolbar and part of the code was theoretical. My major concern was to build the array of paths. With REDIM PRESERVE apparently the memory changed and some of the pointers stored in the listbox where no longer valid. This is why I first get the number of files to dimension the array.
Sounds good. I was wondering why you did that with the array. Better safe than sorry if REDIM PRESERVE is causing a problem.
The only other difference I coded was to NOT set the save point after loading the template file. This keeps the file buffer dirty (and displays the "*" indicator) so that the user can either save the file as the default "UNTITLED1.BAS", "UNTITLED2.BAS", "UNTITLED3.BAS", etc.. or Save As a different name.
The templating system you have made is pretty cool.
> The only other difference I coded was to NOT set the save point after loading the template file.
This is because my editor allows to compile and run code that does not use resources without saving it first. The editor saves it to a temporary file before compiling it. If I don't set the save point, the editor will ask me to save the file. It is very useful when doing small tests.
The number 2 indicates the number of lines before the description:
FBGUI
.bas
FBGUI means that it is a gui program, and FBCON that it is a console program. I use it to know if I have to call the compiler with the gui or console switch.
The .bas line is to indicate the type of program: .bas for basic code, .xml for manifests, etc.