Working on BASIC code editor.
This commit is contained in:
parent
82939c3f27
commit
1fb8e2a387
12 changed files with 745 additions and 115 deletions
|
|
@ -800,6 +800,11 @@ BasFormT *basFormRtLoadFrm(BasFormRtT *rt, const char *source, int32_t sourceLen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fire the Load event now that the form and controls are ready
|
||||||
|
if (form) {
|
||||||
|
basFormRtFireEvent(rt, form, form->name, "Load");
|
||||||
|
}
|
||||||
|
|
||||||
return form;
|
return form;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -870,8 +875,6 @@ void basFormRtShowForm(void *ctx, void *formRef, bool modal) {
|
||||||
if (modal) {
|
if (modal) {
|
||||||
rt->ctx->modalWindow = form->window;
|
rt->ctx->modalWindow = form->window;
|
||||||
}
|
}
|
||||||
|
|
||||||
basFormRtFireEvent(rt, form, form->name, "Load");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -273,6 +273,7 @@ const char *dsgnDefaultEvent(const char *typeName) {
|
||||||
void dsgnFree(DsgnStateT *ds) {
|
void dsgnFree(DsgnStateT *ds) {
|
||||||
if (ds->form) {
|
if (ds->form) {
|
||||||
arrfree(ds->form->controls);
|
arrfree(ds->form->controls);
|
||||||
|
free(ds->form->code);
|
||||||
free(ds->form);
|
free(ds->form);
|
||||||
ds->form = NULL;
|
ds->form = NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -433,6 +434,28 @@ bool dsgnLoadFrm(DsgnStateT *ds, const char *source, int32_t sourceLen) {
|
||||||
curCtrl = NULL;
|
curCtrl = NULL;
|
||||||
} else {
|
} else {
|
||||||
inForm = false;
|
inForm = false;
|
||||||
|
|
||||||
|
// Everything after the form's closing End is code
|
||||||
|
if (pos < end) {
|
||||||
|
// Skip leading blank lines
|
||||||
|
const char *codeStart = pos;
|
||||||
|
|
||||||
|
while (codeStart < end && (*codeStart == '\r' || *codeStart == '\n' || *codeStart == ' ' || *codeStart == '\t')) {
|
||||||
|
codeStart++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (codeStart < end) {
|
||||||
|
int32_t codeLen = (int32_t)(end - codeStart);
|
||||||
|
form->code = (char *)malloc(codeLen + 1);
|
||||||
|
|
||||||
|
if (form->code) {
|
||||||
|
memcpy(form->code, codeStart, codeLen);
|
||||||
|
form->code[codeLen] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break; // done parsing
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -919,6 +942,31 @@ int32_t dsgnSaveFrm(const DsgnStateT *ds, char *buf, int32_t bufSize) {
|
||||||
pos = saveControls(ds->form, buf, bufSize, pos, "", 1);
|
pos = saveControls(ds->form, buf, bufSize, pos, "", 1);
|
||||||
|
|
||||||
pos += snprintf(buf + pos, bufSize - pos, "End\n");
|
pos += snprintf(buf + pos, bufSize - pos, "End\n");
|
||||||
|
|
||||||
|
// Append code section if present
|
||||||
|
if (ds->form->code && ds->form->code[0]) {
|
||||||
|
int32_t codeLen = (int32_t)strlen(ds->form->code);
|
||||||
|
int32_t avail = bufSize - pos - 2; // room for \n prefix and \n suffix
|
||||||
|
|
||||||
|
if (avail > 0) {
|
||||||
|
buf[pos++] = '\n';
|
||||||
|
|
||||||
|
if (codeLen > avail) {
|
||||||
|
codeLen = avail;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(buf + pos, ds->form->code, codeLen);
|
||||||
|
pos += codeLen;
|
||||||
|
|
||||||
|
// Ensure trailing newline
|
||||||
|
if (pos > 0 && buf[pos - 1] != '\n' && pos < bufSize - 1) {
|
||||||
|
buf[pos++] = '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[pos] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,7 @@ typedef struct {
|
||||||
DsgnControlT *controls; // stb_ds dynamic array
|
DsgnControlT *controls; // stb_ds dynamic array
|
||||||
bool dirty;
|
bool dirty;
|
||||||
WidgetT *contentBox; // VBox parent for live widgets
|
WidgetT *contentBox; // VBox parent for live widgets
|
||||||
|
char *code; // BASIC code section (malloc'd, after End block)
|
||||||
} DsgnFormT;
|
} DsgnFormT;
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,13 @@ static void buildWindow(void);
|
||||||
static void clearOutput(void);
|
static void clearOutput(void);
|
||||||
static void compileAndRun(void);
|
static void compileAndRun(void);
|
||||||
static void ensureProject(const char *filePath);
|
static void ensureProject(const char *filePath);
|
||||||
|
static void freeProcBufs(void);
|
||||||
|
static const char *getFullSource(void);
|
||||||
static void loadFile(void);
|
static void loadFile(void);
|
||||||
|
static void parseProcs(const char *source);
|
||||||
|
static void saveActiveFile(void);
|
||||||
|
static void saveCurProc(void);
|
||||||
|
static void showProc(int32_t procIdx);
|
||||||
static int32_t toolbarBottom(void);
|
static int32_t toolbarBottom(void);
|
||||||
static void loadFilePath(const char *path);
|
static void loadFilePath(const char *path);
|
||||||
static void newProject(void);
|
static void newProject(void);
|
||||||
|
|
@ -112,6 +118,7 @@ static bool hasUnsavedData(void);
|
||||||
static bool promptAndSave(void);
|
static bool promptAndSave(void);
|
||||||
static void cleanupFormWin(void);
|
static void cleanupFormWin(void);
|
||||||
static void onClose(WindowT *win);
|
static void onClose(WindowT *win);
|
||||||
|
static void onCodeWinClose(WindowT *win);
|
||||||
static void onContentFocus(WindowT *win);
|
static void onContentFocus(WindowT *win);
|
||||||
static void onFormWinClose(WindowT *win);
|
static void onFormWinClose(WindowT *win);
|
||||||
static void onFormWinResize(WindowT *win, int32_t newW, int32_t newH);
|
static void onFormWinResize(WindowT *win, int32_t newW, int32_t newH);
|
||||||
|
|
@ -180,6 +187,13 @@ static char sOutputBuf[IDE_MAX_OUTPUT];
|
||||||
static int32_t sOutputLen = 0;
|
static int32_t sOutputLen = 0;
|
||||||
static char sFilePath[DVX_MAX_PATH];
|
static char sFilePath[DVX_MAX_PATH];
|
||||||
|
|
||||||
|
// Procedure view state -- the editor shows one procedure at a time.
|
||||||
|
// Each procedure is stored in its own malloc'd buffer. The editor
|
||||||
|
// swaps directly between buffers with no splicing needed.
|
||||||
|
static char *sGeneralBuf = NULL; // (General) section: module-level code
|
||||||
|
static char **sProcBufs = NULL; // stb_ds array: one buffer per procedure
|
||||||
|
static int32_t sCurProcIdx = -2; // which buffer is in the editor (-1=General, -2=none)
|
||||||
|
|
||||||
// Procedure table for Object/Event dropdowns
|
// Procedure table for Object/Event dropdowns
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char objName[64];
|
char objName[64];
|
||||||
|
|
@ -589,9 +603,30 @@ static void clearOutput(void) {
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
static void compileAndRun(void) {
|
static void compileAndRun(void) {
|
||||||
// Save all files before compiling if Save on Run is enabled
|
// Save all dirty files before compiling if Save on Run is enabled
|
||||||
if (sWin && sWin->menuBar && wmMenuItemIsChecked(sWin->menuBar, CMD_SAVE_ON_RUN)) {
|
if (sWin && sWin->menuBar && wmMenuItemIsChecked(sWin->menuBar, CMD_SAVE_ON_RUN)) {
|
||||||
saveFile();
|
if (sProject.activeFileIdx >= 0) {
|
||||||
|
saveActiveFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int32_t i = 0; i < sProject.fileCount; i++) {
|
||||||
|
if (i == sProject.activeFileIdx) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sProject.files[i].modified && sProject.files[i].buffer) {
|
||||||
|
char fullPath[DVX_MAX_PATH];
|
||||||
|
prjFullPath(&sProject, i, fullPath, sizeof(fullPath));
|
||||||
|
|
||||||
|
FILE *f = fopen(fullPath, "w");
|
||||||
|
|
||||||
|
if (f) {
|
||||||
|
fputs(sProject.files[i].buffer, f);
|
||||||
|
fclose(f);
|
||||||
|
sProject.files[i].modified = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clearOutput();
|
clearOutput();
|
||||||
|
|
@ -606,12 +641,22 @@ static void compileAndRun(void) {
|
||||||
int32_t srcLen = 0;
|
int32_t srcLen = 0;
|
||||||
|
|
||||||
if (sProject.projectPath[0] != '\0' && sProject.fileCount > 0) {
|
if (sProject.projectPath[0] != '\0' && sProject.fileCount > 0) {
|
||||||
// Stash current editor contents into the active file's buffer
|
// Stash current editor state
|
||||||
if (sProject.activeFileIdx >= 0 && sEditor) {
|
if (sProject.activeFileIdx >= 0) {
|
||||||
PrjFileT *cur = &sProject.files[sProject.activeFileIdx];
|
PrjFileT *cur = &sProject.files[sProject.activeFileIdx];
|
||||||
const char *edSrc = wgtGetText(sEditor);
|
|
||||||
|
if (!cur->isForm) {
|
||||||
|
const char *fullSrc = getFullSource();
|
||||||
free(cur->buffer);
|
free(cur->buffer);
|
||||||
cur->buffer = edSrc ? strdup(edSrc) : NULL;
|
cur->buffer = fullSrc ? strdup(fullSrc) : NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stash form code if editing a form's code
|
||||||
|
if (sDesigner.form && sCurProcIdx >= -1) {
|
||||||
|
saveCurProc();
|
||||||
|
free(sDesigner.form->code);
|
||||||
|
sDesigner.form->code = strdup(getFullSource());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Concatenate all .bas files from buffers (or disk if not yet loaded)
|
// Concatenate all .bas files from buffers (or disk if not yet loaded)
|
||||||
|
|
@ -629,12 +674,40 @@ static void compileAndRun(void) {
|
||||||
sProject.sourceMapCount = 0;
|
sProject.sourceMapCount = 0;
|
||||||
|
|
||||||
for (int32_t i = 0; i < sProject.fileCount; i++) {
|
for (int32_t i = 0; i < sProject.fileCount; i++) {
|
||||||
|
const char *fileSrc = NULL;
|
||||||
|
char *diskBuf = NULL;
|
||||||
|
|
||||||
if (sProject.files[i].isForm) {
|
if (sProject.files[i].isForm) {
|
||||||
continue;
|
// For .frm files, extract just the code section.
|
||||||
|
// If this is the active form in the designer, use form->code.
|
||||||
|
if (sDesigner.form && i == sProject.activeFileIdx) {
|
||||||
|
fileSrc = sDesigner.form->code;
|
||||||
|
} else if (sProject.files[i].buffer) {
|
||||||
|
// Extract code from the stashed .frm text (after "End\n")
|
||||||
|
const char *buf = sProject.files[i].buffer;
|
||||||
|
const char *endTag = strstr(buf, "\nEnd\n");
|
||||||
|
|
||||||
|
if (!endTag) {
|
||||||
|
endTag = strstr(buf, "\nEnd\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *fileSrc = sProject.files[i].buffer;
|
if (endTag) {
|
||||||
char *diskBuf = NULL;
|
endTag += 5;
|
||||||
|
|
||||||
|
while (*endTag == '\r' || *endTag == '\n') {
|
||||||
|
endTag++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*endTag) {
|
||||||
|
fileSrc = endTag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no code found from memory, fall through to disk read
|
||||||
|
} else {
|
||||||
|
fileSrc = sProject.files[i].buffer;
|
||||||
|
}
|
||||||
|
|
||||||
if (!fileSrc) {
|
if (!fileSrc) {
|
||||||
// Not yet loaded into memory -- read from disk
|
// Not yet loaded into memory -- read from disk
|
||||||
|
|
@ -658,6 +731,27 @@ static void compileAndRun(void) {
|
||||||
int32_t br = (int32_t)fread(diskBuf, 1, size, f);
|
int32_t br = (int32_t)fread(diskBuf, 1, size, f);
|
||||||
diskBuf[br] = '\0';
|
diskBuf[br] = '\0';
|
||||||
fileSrc = diskBuf;
|
fileSrc = diskBuf;
|
||||||
|
|
||||||
|
// For .frm from disk, extract code section
|
||||||
|
if (sProject.files[i].isForm) {
|
||||||
|
const char *endTag = strstr(fileSrc, "\nEnd\n");
|
||||||
|
|
||||||
|
if (!endTag) {
|
||||||
|
endTag = strstr(fileSrc, "\nEnd\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endTag) {
|
||||||
|
endTag += 5;
|
||||||
|
|
||||||
|
while (*endTag == '\r' || *endTag == '\n') {
|
||||||
|
endTag++;
|
||||||
|
}
|
||||||
|
|
||||||
|
fileSrc = endTag;
|
||||||
|
} else {
|
||||||
|
fileSrc = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -708,8 +802,8 @@ static void compileAndRun(void) {
|
||||||
src = concatBuf;
|
src = concatBuf;
|
||||||
srcLen = pos;
|
srcLen = pos;
|
||||||
} else {
|
} else {
|
||||||
// No project files -- compile whatever is in the editor
|
// No project files -- compile the full source
|
||||||
src = wgtGetText(sEditor);
|
src = getFullSource();
|
||||||
|
|
||||||
if (!src || *src == '\0') {
|
if (!src || *src == '\0') {
|
||||||
setStatus("No source code to run.");
|
setStatus("No source code to run.");
|
||||||
|
|
@ -823,6 +917,11 @@ static void runModule(BasModuleT *mod) {
|
||||||
// Load any .frm files from the same directory as the source
|
// Load any .frm files from the same directory as the source
|
||||||
loadFrmFiles(formRt);
|
loadFrmFiles(formRt);
|
||||||
|
|
||||||
|
// Auto-show the first form (like VB3's startup form)
|
||||||
|
if (formRt->formCount > 0) {
|
||||||
|
basFormRtShowForm(formRt, &formRt->forms[0], false);
|
||||||
|
}
|
||||||
|
|
||||||
sVm = vm;
|
sVm = vm;
|
||||||
|
|
||||||
// Run in slices of 10000 steps, yielding to DVX between slices
|
// Run in slices of 10000 steps, yielding to DVX between slices
|
||||||
|
|
@ -1037,9 +1136,8 @@ static void loadFilePath(const char *path) {
|
||||||
showCodeWindow();
|
showCodeWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sEditor) {
|
// Parse into per-procedure buffers and show (General) section
|
||||||
wgtSetText(sEditor, sSourceBuf);
|
parseProcs(sSourceBuf);
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(sFilePath, sizeof(sFilePath), "%s", path);
|
snprintf(sFilePath, sizeof(sFilePath), "%s", path);
|
||||||
|
|
||||||
|
|
@ -1050,6 +1148,7 @@ static void loadFilePath(const char *path) {
|
||||||
|
|
||||||
dsgnFree(&sDesigner);
|
dsgnFree(&sDesigner);
|
||||||
updateDropdowns();
|
updateDropdowns();
|
||||||
|
showProc(-1); // show (General) section
|
||||||
setStatus("File loaded.");
|
setStatus("File loaded.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1118,10 +1217,10 @@ static void ensureProject(const char *filePath) {
|
||||||
snprintf(frmPath, sizeof(frmPath), "%s", filePath);
|
snprintf(frmPath, sizeof(frmPath), "%s", filePath);
|
||||||
char *frmDot = strrchr(frmPath, '.');
|
char *frmDot = strrchr(frmPath, '.');
|
||||||
|
|
||||||
if (frmDot) {
|
if (frmDot && (frmDot - frmPath) + 4 < DVX_MAX_PATH) {
|
||||||
strcpy(frmDot, ".frm");
|
snprintf(frmDot, sizeof(frmPath) - (frmDot - frmPath), ".frm");
|
||||||
} else {
|
} else if ((int32_t)strlen(frmPath) + 4 < DVX_MAX_PATH) {
|
||||||
strcat(frmPath, ".frm");
|
snprintf(frmPath + strlen(frmPath), sizeof(frmPath) - strlen(frmPath), ".frm");
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE *frmFile = fopen(frmPath, "r");
|
FILE *frmFile = fopen(frmPath, "r");
|
||||||
|
|
@ -1202,6 +1301,13 @@ static void saveActiveFile(void) {
|
||||||
prjFullPath(&sProject, idx, fullPath, sizeof(fullPath));
|
prjFullPath(&sProject, idx, fullPath, sizeof(fullPath));
|
||||||
|
|
||||||
if (file->isForm && sDesigner.form) {
|
if (file->isForm && sDesigner.form) {
|
||||||
|
// Save editor code back to form->code before saving
|
||||||
|
if (sCurProcIdx >= -1) {
|
||||||
|
saveCurProc();
|
||||||
|
free(sDesigner.form->code);
|
||||||
|
sDesigner.form->code = strdup(getFullSource());
|
||||||
|
}
|
||||||
|
|
||||||
// Save form designer state to .frm file
|
// Save form designer state to .frm file
|
||||||
char *frmBuf = (char *)malloc(IDE_MAX_SOURCE);
|
char *frmBuf = (char *)malloc(IDE_MAX_SOURCE);
|
||||||
|
|
||||||
|
|
@ -1221,9 +1327,9 @@ static void saveActiveFile(void) {
|
||||||
|
|
||||||
free(frmBuf);
|
free(frmBuf);
|
||||||
}
|
}
|
||||||
} else if (!file->isForm && sEditor) {
|
} else if (!file->isForm) {
|
||||||
// Save code editor contents to .bas file
|
// Save full source (splice current proc back first)
|
||||||
const char *src = wgtGetText(sEditor);
|
const char *src = getFullSource();
|
||||||
|
|
||||||
if (src) {
|
if (src) {
|
||||||
FILE *f = fopen(fullPath, "w");
|
FILE *f = fopen(fullPath, "w");
|
||||||
|
|
@ -1335,9 +1441,9 @@ static void onPrjFileClick(int32_t fileIdx, bool isForm) {
|
||||||
|
|
||||||
cur->modified = true;
|
cur->modified = true;
|
||||||
}
|
}
|
||||||
} else if (!cur->isForm && sEditor) {
|
} else if (!cur->isForm) {
|
||||||
// Stash code editor text
|
// Stash full source (splice current proc back first)
|
||||||
const char *src = wgtGetText(sEditor);
|
const char *src = getFullSource();
|
||||||
free(cur->buffer);
|
free(cur->buffer);
|
||||||
cur->buffer = src ? strdup(src) : NULL;
|
cur->buffer = src ? strdup(src) : NULL;
|
||||||
cur->modified = true;
|
cur->modified = true;
|
||||||
|
|
@ -1436,9 +1542,9 @@ static void onPrjFileClick(int32_t fileIdx, bool isForm) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target->buffer) {
|
if (target->buffer) {
|
||||||
if (sEditor) {
|
parseProcs(target->buffer);
|
||||||
wgtSetText(sEditor, target->buffer);
|
updateDropdowns();
|
||||||
}
|
showProc(-1);
|
||||||
} else {
|
} else {
|
||||||
char fullPath[DVX_MAX_PATH];
|
char fullPath[DVX_MAX_PATH];
|
||||||
prjFullPath(&sProject, fileIdx, fullPath, sizeof(fullPath));
|
prjFullPath(&sProject, fileIdx, fullPath, sizeof(fullPath));
|
||||||
|
|
@ -1448,9 +1554,11 @@ static void onPrjFileClick(int32_t fileIdx, bool isForm) {
|
||||||
if (f) {
|
if (f) {
|
||||||
fclose(f);
|
fclose(f);
|
||||||
loadFilePath(fullPath);
|
loadFilePath(fullPath);
|
||||||
} else if (sEditor) {
|
} else {
|
||||||
// File doesn't exist yet -- start with empty editor
|
// File doesn't exist yet -- start with empty source
|
||||||
wgtSetText(sEditor, "");
|
parseProcs("");
|
||||||
|
updateDropdowns();
|
||||||
|
showProc(-1);
|
||||||
target->modified = true;
|
target->modified = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1735,28 +1843,46 @@ void ideRenameInCode(const char *oldName, const char *newName) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rename in the active editor
|
// Rename in the per-procedure buffers (form code currently being edited)
|
||||||
if (sEditor && sProject.activeFileIdx >= 0 &&
|
if (sGeneralBuf) {
|
||||||
!sProject.files[sProject.activeFileIdx].isForm) {
|
char *replaced = renameInBuffer(sGeneralBuf, oldName, newName);
|
||||||
const char *edText = wgtGetText(sEditor);
|
|
||||||
|
|
||||||
if (edText) {
|
|
||||||
char *replaced = renameInBuffer(edText, oldName, newName);
|
|
||||||
|
|
||||||
if (replaced) {
|
if (replaced) {
|
||||||
wgtSetText(sEditor, replaced);
|
free(sGeneralBuf);
|
||||||
free(replaced);
|
sGeneralBuf = replaced;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int32_t i = 0; i < (int32_t)arrlen(sProcBufs); i++) {
|
||||||
|
if (sProcBufs[i]) {
|
||||||
|
char *replaced = renameInBuffer(sProcBufs[i], oldName, newName);
|
||||||
|
|
||||||
|
if (replaced) {
|
||||||
|
free(sProcBufs[i]);
|
||||||
|
sProcBufs[i] = replaced;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rename in all project .bas file buffers
|
// Update the editor if it's showing a procedure
|
||||||
|
if (sEditor && sCurProcIdx >= -1) {
|
||||||
|
if (sCurProcIdx == -1 && sGeneralBuf) {
|
||||||
|
wgtSetText(sEditor, sGeneralBuf);
|
||||||
|
} else if (sCurProcIdx >= 0 && sCurProcIdx < (int32_t)arrlen(sProcBufs)) {
|
||||||
|
wgtSetText(sEditor, sProcBufs[sCurProcIdx]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update form->code from the renamed buffers
|
||||||
|
if (sDesigner.form) {
|
||||||
|
free(sDesigner.form->code);
|
||||||
|
sDesigner.form->code = strdup(getFullSource());
|
||||||
|
sDesigner.form->dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rename in all project .bas file buffers (and non-active .frm code)
|
||||||
for (int32_t i = 0; i < sProject.fileCount; i++) {
|
for (int32_t i = 0; i < sProject.fileCount; i++) {
|
||||||
if (sProject.files[i].isForm) {
|
// Skip the active file (already handled above)
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip the active file (already handled via editor)
|
|
||||||
if (i == sProject.activeFileIdx) {
|
if (i == sProject.activeFileIdx) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -1804,6 +1930,34 @@ void ideRenameInCode(const char *oldName, const char *newName) {
|
||||||
sProject.files[i].modified = true;
|
sProject.files[i].modified = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Refresh dropdowns to reflect renamed procedures
|
||||||
|
updateDropdowns();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// onCodeWinClose -- user closed the code window via X button
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
static void onCodeWinClose(WindowT *win) {
|
||||||
|
// Stash code back to form->code before the window is destroyed
|
||||||
|
if (sDesigner.form && sCurProcIdx >= -1) {
|
||||||
|
saveCurProc();
|
||||||
|
free(sDesigner.form->code);
|
||||||
|
sDesigner.form->code = strdup(getFullSource());
|
||||||
|
sDesigner.form->dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
dvxDestroyWindow(sAc, win);
|
||||||
|
sCodeWin = NULL;
|
||||||
|
sEditor = NULL;
|
||||||
|
sObjDropdown = NULL;
|
||||||
|
sEvtDropdown = NULL;
|
||||||
|
|
||||||
|
if (sLastFocusWin == win) {
|
||||||
|
sLastFocusWin = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1821,9 +1975,8 @@ static void onProjectWinClose(WindowT *win) {
|
||||||
// loadFrmFiles
|
// loadFrmFiles
|
||||||
// ============================================================
|
// ============================================================
|
||||||
//
|
//
|
||||||
// Try to load a .frm file with the same base name as the loaded
|
// Load all .frm files listed in the current project into the
|
||||||
// .bas source file. For example, if the user loaded "clickme.bas",
|
// form runtime for execution.
|
||||||
// this looks for "clickme.frm" in the same directory.
|
|
||||||
|
|
||||||
static void loadFrmFile(BasFormRtT *rt, const char *frmPath) {
|
static void loadFrmFile(BasFormRtT *rt, const char *frmPath) {
|
||||||
FILE *f = fopen(frmPath, "r");
|
FILE *f = fopen(frmPath, "r");
|
||||||
|
|
@ -1852,14 +2005,7 @@ static void loadFrmFile(BasFormRtT *rt, const char *frmPath) {
|
||||||
fclose(f);
|
fclose(f);
|
||||||
frmBuf[bytesRead] = '\0';
|
frmBuf[bytesRead] = '\0';
|
||||||
|
|
||||||
BasFormT *form = basFormRtLoadFrm(rt, frmBuf, bytesRead);
|
basFormRtLoadFrm(rt, frmBuf, bytesRead);
|
||||||
|
|
||||||
if (form) {
|
|
||||||
int32_t pos = sOutputLen;
|
|
||||||
int32_t n = snprintf(sOutputBuf + pos, IDE_MAX_OUTPUT - pos, "Loaded form: %s\n", form->name);
|
|
||||||
sOutputLen += n;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(frmBuf);
|
free(frmBuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2016,6 +2162,8 @@ static void onClose(WindowT *win) {
|
||||||
|
|
||||||
dsgnFree(&sDesigner);
|
dsgnFree(&sDesigner);
|
||||||
|
|
||||||
|
freeProcBufs();
|
||||||
|
|
||||||
arrfree(sProcTable);
|
arrfree(sProcTable);
|
||||||
arrfree(sObjItems);
|
arrfree(sObjItems);
|
||||||
arrfree(sEvtItems);
|
arrfree(sEvtItems);
|
||||||
|
|
@ -2297,7 +2445,7 @@ static void onEvtDropdownChange(WidgetT *w) {
|
||||||
for (int32_t i = 0; i < procCount; i++) {
|
for (int32_t i = 0; i < procCount; i++) {
|
||||||
if (strcasecmp(sProcTable[i].objName, selObj) == 0) {
|
if (strcasecmp(sProcTable[i].objName, selObj) == 0) {
|
||||||
if (matchIdx == evtIdx) {
|
if (matchIdx == evtIdx) {
|
||||||
wgtTextAreaGoToLine(sEditor, sProcTable[i].lineNum);
|
showProc(i);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2528,9 +2676,89 @@ static int32_t onFormWinCursorQuery(WindowT *win, int32_t x, int32_t y) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// navigateToEventSub -- open code editor at the default event sub for the
|
||||||
|
// selected control (or form). Creates the sub skeleton if it doesn't exist.
|
||||||
|
// Code is stored in the .frm file's code section (sDesigner.form->code).
|
||||||
|
|
||||||
|
static void navigateToEventSub(void) {
|
||||||
|
if (!sDesigner.form) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine control name and default event
|
||||||
|
const char *ctrlName = NULL;
|
||||||
|
const char *eventName = NULL;
|
||||||
|
|
||||||
|
if (sDesigner.selectedIdx >= 0 &&
|
||||||
|
sDesigner.selectedIdx < (int32_t)arrlen(sDesigner.form->controls)) {
|
||||||
|
DsgnControlT *ctrl = &sDesigner.form->controls[sDesigner.selectedIdx];
|
||||||
|
ctrlName = ctrl->name;
|
||||||
|
eventName = dsgnDefaultEvent(ctrl->typeName);
|
||||||
|
} else {
|
||||||
|
ctrlName = sDesigner.form->name;
|
||||||
|
eventName = dsgnDefaultEvent("Form");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ctrlName || !eventName) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char subName[128];
|
||||||
|
snprintf(subName, sizeof(subName), "%s_%s", ctrlName, eventName);
|
||||||
|
|
||||||
|
// Parse the form's code into per-procedure buffers
|
||||||
|
parseProcs(sDesigner.form->code ? sDesigner.form->code : "");
|
||||||
|
|
||||||
|
// Ensure code window is open
|
||||||
|
if (!sCodeWin) {
|
||||||
|
showCodeWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sEditor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDropdowns();
|
||||||
|
|
||||||
|
// Search for existing procedure
|
||||||
|
int32_t procCount = (int32_t)arrlen(sProcTable);
|
||||||
|
|
||||||
|
for (int32_t i = 0; i < procCount; i++) {
|
||||||
|
char fullName[128];
|
||||||
|
snprintf(fullName, sizeof(fullName), "%s_%s", sProcTable[i].objName, sProcTable[i].evtName);
|
||||||
|
|
||||||
|
if (strcasecmp(fullName, subName) == 0) {
|
||||||
|
switchToCode();
|
||||||
|
showProc(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not found -- create a new sub and add it as a procedure buffer
|
||||||
|
char skeleton[256];
|
||||||
|
snprintf(skeleton, sizeof(skeleton), "Sub %s ()\n\nEnd Sub\n", subName);
|
||||||
|
|
||||||
|
char *newBuf = strdup(skeleton);
|
||||||
|
arrput(sProcBufs, newBuf);
|
||||||
|
|
||||||
|
sDesigner.form->dirty = true;
|
||||||
|
|
||||||
|
// Save code back to form
|
||||||
|
free(sDesigner.form->code);
|
||||||
|
sDesigner.form->code = strdup(getFullSource());
|
||||||
|
|
||||||
|
updateDropdowns();
|
||||||
|
|
||||||
|
// Show the new procedure (it's the last one)
|
||||||
|
switchToCode();
|
||||||
|
showProc((int32_t)arrlen(sProcBufs) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void onFormWinMouse(WindowT *win, int32_t x, int32_t y, int32_t buttons) {
|
static void onFormWinMouse(WindowT *win, int32_t x, int32_t y, int32_t buttons) {
|
||||||
(void)win;
|
(void)win;
|
||||||
static int32_t lastButtons = 0;
|
static int32_t lastButtons = 0;
|
||||||
|
|
||||||
bool wasDown = (lastButtons & MOUSE_LEFT) != 0;
|
bool wasDown = (lastButtons & MOUSE_LEFT) != 0;
|
||||||
bool isDown = (buttons & MOUSE_LEFT) != 0;
|
bool isDown = (buttons & MOUSE_LEFT) != 0;
|
||||||
|
|
||||||
|
|
@ -2539,15 +2767,15 @@ static void onFormWinMouse(WindowT *win, int32_t x, int32_t y, int32_t buttons)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isDown) {
|
if (isDown && !wasDown) {
|
||||||
int32_t prevCount = sDesigner.form ? (int32_t)arrlen(sDesigner.form->controls) : 0;
|
// Detect double-click using the system-wide setting
|
||||||
bool wasDirty = sDesigner.form ? sDesigner.form->dirty : false;
|
int32_t clicks = multiClickDetect(x, y);
|
||||||
bool drag = wasDown;
|
|
||||||
dsgnOnMouse(&sDesigner, x, y, drag);
|
|
||||||
|
|
||||||
// Rebuild tree if controls were added, removed, or reordered
|
int32_t prevCount = (int32_t)arrlen(sDesigner.form->controls);
|
||||||
int32_t newCount = sDesigner.form ? (int32_t)arrlen(sDesigner.form->controls) : 0;
|
bool wasDirty = sDesigner.form->dirty;
|
||||||
bool nowDirty = sDesigner.form ? sDesigner.form->dirty : false;
|
dsgnOnMouse(&sDesigner, x, y, false);
|
||||||
|
int32_t newCount = (int32_t)arrlen(sDesigner.form->controls);
|
||||||
|
bool nowDirty = sDesigner.form->dirty;
|
||||||
|
|
||||||
if (newCount != prevCount || (nowDirty && !wasDirty)) {
|
if (newCount != prevCount || (nowDirty && !wasDirty)) {
|
||||||
prpRebuildTree(&sDesigner);
|
prpRebuildTree(&sDesigner);
|
||||||
|
|
@ -2558,7 +2786,20 @@ static void onFormWinMouse(WindowT *win, int32_t x, int32_t y, int32_t buttons)
|
||||||
if (sFormWin) {
|
if (sFormWin) {
|
||||||
dvxInvalidateWindow(sAc, sFormWin);
|
dvxInvalidateWindow(sAc, sFormWin);
|
||||||
}
|
}
|
||||||
} else if (wasDown) {
|
|
||||||
|
if (clicks >= 2 && sDesigner.activeTool[0] == '\0') {
|
||||||
|
navigateToEventSub();
|
||||||
|
}
|
||||||
|
} else if (isDown && wasDown) {
|
||||||
|
// Drag
|
||||||
|
dsgnOnMouse(&sDesigner, x, y, true);
|
||||||
|
prpRefresh(&sDesigner);
|
||||||
|
|
||||||
|
if (sFormWin) {
|
||||||
|
dvxInvalidateWindow(sAc, sFormWin);
|
||||||
|
}
|
||||||
|
} else if (!isDown && wasDown) {
|
||||||
|
// Release
|
||||||
dsgnOnMouse(&sDesigner, x, y, false);
|
dsgnOnMouse(&sDesigner, x, y, false);
|
||||||
prpRefresh(&sDesigner);
|
prpRefresh(&sDesigner);
|
||||||
|
|
||||||
|
|
@ -2647,11 +2888,34 @@ static void onFormWinClose(WindowT *win) {
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
static void switchToCode(void) {
|
static void switchToCode(void) {
|
||||||
if (sFormWin) {
|
// Stash form data
|
||||||
dvxDestroyWindow(sAc, sFormWin);
|
if (sDesigner.form && sProject.activeFileIdx >= 0) {
|
||||||
cleanupFormWin();
|
PrjFileT *cur = &sProject.files[sProject.activeFileIdx];
|
||||||
|
|
||||||
|
if (cur->isForm) {
|
||||||
|
char *frmBuf = (char *)malloc(IDE_MAX_SOURCE);
|
||||||
|
|
||||||
|
if (frmBuf) {
|
||||||
|
int32_t frmLen = dsgnSaveFrm(&sDesigner, frmBuf, IDE_MAX_SOURCE);
|
||||||
|
|
||||||
|
free(cur->buffer);
|
||||||
|
|
||||||
|
if (frmLen > 0) {
|
||||||
|
frmBuf[frmLen] = '\0';
|
||||||
|
cur->buffer = frmBuf;
|
||||||
|
} else {
|
||||||
|
free(frmBuf);
|
||||||
|
cur->buffer = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cur->modified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't destroy the form window -- allow both code and design
|
||||||
|
// to be open simultaneously, like VB3.
|
||||||
|
|
||||||
setStatus("Code view.");
|
setStatus("Code view.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2661,6 +2925,13 @@ static void switchToCode(void) {
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
static void switchToDesign(void) {
|
static void switchToDesign(void) {
|
||||||
|
// Save code back to form before switching
|
||||||
|
if (sDesigner.form && sCurProcIdx >= -1) {
|
||||||
|
saveCurProc();
|
||||||
|
free(sDesigner.form->code);
|
||||||
|
sDesigner.form->code = strdup(getFullSource());
|
||||||
|
}
|
||||||
|
|
||||||
// If already open, just bring to front
|
// If already open, just bring to front
|
||||||
if (sFormWin) {
|
if (sFormWin) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -2673,10 +2944,10 @@ static void switchToDesign(void) {
|
||||||
snprintf(frmPath, sizeof(frmPath), "%s", sFilePath);
|
snprintf(frmPath, sizeof(frmPath), "%s", sFilePath);
|
||||||
char *dot = strrchr(frmPath, '.');
|
char *dot = strrchr(frmPath, '.');
|
||||||
|
|
||||||
if (dot) {
|
if (dot && (dot - frmPath) + 4 < DVX_MAX_PATH) {
|
||||||
strcpy(dot, ".frm");
|
snprintf(dot, sizeof(frmPath) - (dot - frmPath), ".frm");
|
||||||
} else {
|
} else if ((int32_t)strlen(frmPath) + 4 < DVX_MAX_PATH) {
|
||||||
strcat(frmPath, ".frm");
|
snprintf(frmPath + strlen(frmPath), sizeof(frmPath) - strlen(frmPath), ".frm");
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE *f = fopen(frmPath, "r");
|
FILE *f = fopen(frmPath, "r");
|
||||||
|
|
@ -2800,14 +3071,20 @@ static void showCodeWindow(void) {
|
||||||
return; // already open
|
return; // already open
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t codeY = sWin ? sWin->y + sWin->h + 2 : 60;
|
int32_t codeY = toolbarBottom();
|
||||||
int32_t codeH = sAc->display.height - codeY - 122;
|
int32_t codeH = sAc->display.height - codeY - 122;
|
||||||
|
|
||||||
sCodeWin = dvxCreateWindow(sAc, "Code", 0, codeY, sAc->display.width, codeH, true);
|
sCodeWin = dvxCreateWindow(sAc, "Code", 0, codeY, sAc->display.width, codeH, true);
|
||||||
|
|
||||||
|
// Ensure position is below the toolbar (dvxCreateWindow may adjust)
|
||||||
|
if (sCodeWin) {
|
||||||
|
sCodeWin->y = codeY;
|
||||||
|
}
|
||||||
|
|
||||||
if (sCodeWin) {
|
if (sCodeWin) {
|
||||||
sCodeWin->onMenu = onMenu;
|
sCodeWin->onMenu = onMenu;
|
||||||
sCodeWin->onFocus = onContentFocus;
|
sCodeWin->onFocus = onContentFocus;
|
||||||
|
sCodeWin->onClose = onCodeWinClose;
|
||||||
sCodeWin->accelTable = sWin ? sWin->accelTable : NULL;
|
sCodeWin->accelTable = sWin ? sWin->accelTable : NULL;
|
||||||
sLastFocusWin = sCodeWin;
|
sLastFocusWin = sCodeWin;
|
||||||
|
|
||||||
|
|
@ -2928,17 +3205,242 @@ static void setStatus(const char *text) {
|
||||||
// the Object and Event dropdowns. Procedure names are split on
|
// the Object and Event dropdowns. Procedure names are split on
|
||||||
// '_' into ObjectName and EventName (e.g. "Command1_Click").
|
// '_' into ObjectName and EventName (e.g. "Command1_Click").
|
||||||
|
|
||||||
|
// freeProcBufs -- release all procedure buffers
|
||||||
|
|
||||||
|
static void freeProcBufs(void) {
|
||||||
|
free(sGeneralBuf);
|
||||||
|
sGeneralBuf = NULL;
|
||||||
|
|
||||||
|
for (int32_t i = 0; i < (int32_t)arrlen(sProcBufs); i++) {
|
||||||
|
free(sProcBufs[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
arrfree(sProcBufs);
|
||||||
|
sProcBufs = NULL;
|
||||||
|
sCurProcIdx = -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// parseProcs -- split source into (General) + per-procedure buffers
|
||||||
|
|
||||||
|
static void parseProcs(const char *source) {
|
||||||
|
freeProcBufs();
|
||||||
|
|
||||||
|
if (!source) {
|
||||||
|
sGeneralBuf = strdup("");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *pos = source;
|
||||||
|
const char *genEnd = source; // end of (General) section
|
||||||
|
|
||||||
|
while (*pos) {
|
||||||
|
const char *lineStart = pos;
|
||||||
|
|
||||||
|
// Skip leading whitespace
|
||||||
|
const char *trimmed = pos;
|
||||||
|
|
||||||
|
while (*trimmed == ' ' || *trimmed == '\t') {
|
||||||
|
trimmed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isSub = (strncasecmp(trimmed, "SUB ", 4) == 0);
|
||||||
|
bool isFunc = (strncasecmp(trimmed, "FUNCTION ", 9) == 0);
|
||||||
|
|
||||||
|
if (isSub || isFunc) {
|
||||||
|
// On first proc, mark end of (General) section
|
||||||
|
if (arrlen(sProcBufs) == 0) {
|
||||||
|
genEnd = lineStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find End Sub / End Function
|
||||||
|
const char *endTag = isSub ? "END SUB" : "END FUNCTION";
|
||||||
|
int32_t endTagLen = isSub ? 7 : 12;
|
||||||
|
const char *scan = pos;
|
||||||
|
|
||||||
|
// Advance past the Sub/Function line
|
||||||
|
while (*scan && *scan != '\n') { scan++; }
|
||||||
|
if (*scan == '\n') { scan++; }
|
||||||
|
|
||||||
|
// Scan for End Sub/Function
|
||||||
|
while (*scan) {
|
||||||
|
const char *sl = scan;
|
||||||
|
|
||||||
|
while (*sl == ' ' || *sl == '\t') { sl++; }
|
||||||
|
|
||||||
|
if (strncasecmp(sl, endTag, endTagLen) == 0) {
|
||||||
|
while (*scan && *scan != '\n') { scan++; }
|
||||||
|
if (*scan == '\n') { scan++; }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (*scan && *scan != '\n') { scan++; }
|
||||||
|
if (*scan == '\n') { scan++; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract this procedure
|
||||||
|
int32_t procLen = (int32_t)(scan - lineStart);
|
||||||
|
char *procBuf = (char *)malloc(procLen + 1);
|
||||||
|
|
||||||
|
if (procBuf) {
|
||||||
|
memcpy(procBuf, lineStart, procLen);
|
||||||
|
procBuf[procLen] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
arrput(sProcBufs, procBuf);
|
||||||
|
pos = scan;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advance to next line
|
||||||
|
while (*pos && *pos != '\n') { pos++; }
|
||||||
|
if (*pos == '\n') { pos++; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract (General) section
|
||||||
|
int32_t genLen = (int32_t)(genEnd - source);
|
||||||
|
|
||||||
|
// Trim trailing blank lines
|
||||||
|
while (genLen > 0 && (source[genLen - 1] == '\n' || source[genLen - 1] == '\r' ||
|
||||||
|
source[genLen - 1] == ' ' || source[genLen - 1] == '\t')) {
|
||||||
|
genLen--;
|
||||||
|
}
|
||||||
|
|
||||||
|
sGeneralBuf = (char *)malloc(genLen + 2);
|
||||||
|
|
||||||
|
if (sGeneralBuf) {
|
||||||
|
memcpy(sGeneralBuf, source, genLen);
|
||||||
|
sGeneralBuf[genLen] = '\n';
|
||||||
|
sGeneralBuf[genLen + 1] = '\0';
|
||||||
|
|
||||||
|
if (genLen == 0) {
|
||||||
|
sGeneralBuf[0] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// saveCurProc -- save editor contents back to the current buffer
|
||||||
|
|
||||||
|
static void saveCurProc(void) {
|
||||||
|
if (!sEditor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *edText = wgtGetText(sEditor);
|
||||||
|
|
||||||
|
if (!edText) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sCurProcIdx == -1) {
|
||||||
|
free(sGeneralBuf);
|
||||||
|
sGeneralBuf = strdup(edText);
|
||||||
|
} else if (sCurProcIdx >= 0 && sCurProcIdx < (int32_t)arrlen(sProcBufs)) {
|
||||||
|
free(sProcBufs[sCurProcIdx]);
|
||||||
|
sProcBufs[sCurProcIdx] = strdup(edText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// showProc -- display a procedure buffer in the editor
|
||||||
|
|
||||||
|
static void showProc(int32_t procIdx) {
|
||||||
|
if (!sEditor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save whatever is currently in the editor
|
||||||
|
if (sCurProcIdx >= -1) {
|
||||||
|
saveCurProc();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (procIdx == -1) {
|
||||||
|
wgtSetText(sEditor, sGeneralBuf ? sGeneralBuf : "");
|
||||||
|
sCurProcIdx = -1;
|
||||||
|
} else if (procIdx >= 0 && procIdx < (int32_t)arrlen(sProcBufs)) {
|
||||||
|
wgtSetText(sEditor, sProcBufs[procIdx] ? sProcBufs[procIdx] : "");
|
||||||
|
sCurProcIdx = procIdx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// getFullSource -- reassemble all buffers into one source string
|
||||||
|
// Caller must free the returned buffer.
|
||||||
|
|
||||||
|
static char *sFullSourceCache = NULL;
|
||||||
|
|
||||||
|
static const char *getFullSource(void) {
|
||||||
|
saveCurProc();
|
||||||
|
|
||||||
|
free(sFullSourceCache);
|
||||||
|
|
||||||
|
// Calculate total length
|
||||||
|
int32_t totalLen = 0;
|
||||||
|
|
||||||
|
if (sGeneralBuf && sGeneralBuf[0]) {
|
||||||
|
totalLen += (int32_t)strlen(sGeneralBuf);
|
||||||
|
totalLen += 2; // blank line separator
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t procCount = (int32_t)arrlen(sProcBufs);
|
||||||
|
|
||||||
|
for (int32_t i = 0; i < procCount; i++) {
|
||||||
|
if (sProcBufs[i]) {
|
||||||
|
totalLen += (int32_t)strlen(sProcBufs[i]);
|
||||||
|
totalLen += 1; // newline separator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sFullSourceCache = (char *)malloc(totalLen + 1);
|
||||||
|
|
||||||
|
if (!sFullSourceCache) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t pos = 0;
|
||||||
|
|
||||||
|
if (sGeneralBuf && sGeneralBuf[0]) {
|
||||||
|
int32_t len = (int32_t)strlen(sGeneralBuf);
|
||||||
|
memcpy(sFullSourceCache + pos, sGeneralBuf, len);
|
||||||
|
pos += len;
|
||||||
|
|
||||||
|
if (pos > 0 && sFullSourceCache[pos - 1] != '\n') {
|
||||||
|
sFullSourceCache[pos++] = '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
sFullSourceCache[pos++] = '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int32_t i = 0; i < procCount; i++) {
|
||||||
|
if (sProcBufs[i]) {
|
||||||
|
int32_t len = (int32_t)strlen(sProcBufs[i]);
|
||||||
|
memcpy(sFullSourceCache + pos, sProcBufs[i], len);
|
||||||
|
pos += len;
|
||||||
|
|
||||||
|
if (pos > 0 && sFullSourceCache[pos - 1] != '\n') {
|
||||||
|
sFullSourceCache[pos++] = '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sFullSourceCache[pos] = '\0';
|
||||||
|
return sFullSourceCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void updateDropdowns(void) {
|
static void updateDropdowns(void) {
|
||||||
// Reset dynamic arrays
|
// Reset dynamic arrays
|
||||||
arrsetlen(sProcTable, 0);
|
arrsetlen(sProcTable, 0);
|
||||||
arrsetlen(sObjItems, 0);
|
arrsetlen(sObjItems, 0);
|
||||||
arrsetlen(sEvtItems, 0);
|
arrsetlen(sEvtItems, 0);
|
||||||
|
|
||||||
if (!sEditor || !sObjDropdown || !sEvtDropdown) {
|
if (!sObjDropdown || !sEvtDropdown) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *src = wgtGetText(sEditor);
|
// Scan the reassembled full source
|
||||||
|
const char *src = getFullSource();
|
||||||
|
|
||||||
if (!src) {
|
if (!src) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -2949,6 +3451,8 @@ static void updateDropdowns(void) {
|
||||||
int32_t lineNum = 1;
|
int32_t lineNum = 1;
|
||||||
|
|
||||||
while (*pos) {
|
while (*pos) {
|
||||||
|
const char *lineStart = pos;
|
||||||
|
|
||||||
// Skip leading whitespace
|
// Skip leading whitespace
|
||||||
while (*pos == ' ' || *pos == '\t') {
|
while (*pos == ' ' || *pos == '\t') {
|
||||||
pos++;
|
pos++;
|
||||||
|
|
@ -2961,12 +3465,10 @@ static void updateDropdowns(void) {
|
||||||
if (isSub || isFunc) {
|
if (isSub || isFunc) {
|
||||||
pos += isSub ? 4 : 9;
|
pos += isSub ? 4 : 9;
|
||||||
|
|
||||||
// Skip whitespace after keyword
|
|
||||||
while (*pos == ' ' || *pos == '\t') {
|
while (*pos == ' ' || *pos == '\t') {
|
||||||
pos++;
|
pos++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract procedure name
|
|
||||||
char procName[64];
|
char procName[64];
|
||||||
int32_t nameLen = 0;
|
int32_t nameLen = 0;
|
||||||
|
|
||||||
|
|
@ -2976,7 +3478,40 @@ static void updateDropdowns(void) {
|
||||||
|
|
||||||
procName[nameLen] = '\0';
|
procName[nameLen] = '\0';
|
||||||
|
|
||||||
// Split on '_' into object + event
|
// Find End Sub / End Function
|
||||||
|
const char *endTag = isSub ? "END SUB" : "END FUNCTION";
|
||||||
|
int32_t endTagLen = isSub ? 7 : 12;
|
||||||
|
const char *scan = pos;
|
||||||
|
|
||||||
|
while (*scan) {
|
||||||
|
const char *sl = scan;
|
||||||
|
|
||||||
|
while (*sl == ' ' || *sl == '\t') {
|
||||||
|
sl++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strncasecmp(sl, endTag, endTagLen) == 0) {
|
||||||
|
// Advance past the End line
|
||||||
|
while (*scan && *scan != '\n') {
|
||||||
|
scan++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*scan == '\n') {
|
||||||
|
scan++;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (*scan && *scan != '\n') {
|
||||||
|
scan++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*scan == '\n') {
|
||||||
|
scan++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
IdeProcEntryT entry;
|
IdeProcEntryT entry;
|
||||||
memset(&entry, 0, sizeof(entry));
|
memset(&entry, 0, sizeof(entry));
|
||||||
entry.lineNum = lineNum;
|
entry.lineNum = lineNum;
|
||||||
|
|
@ -2999,6 +3534,18 @@ static void updateDropdowns(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
arrput(sProcTable, entry);
|
arrput(sProcTable, entry);
|
||||||
|
|
||||||
|
// Skip to end of this proc (already scanned)
|
||||||
|
pos = scan;
|
||||||
|
|
||||||
|
// Count lines we skipped
|
||||||
|
for (const char *c = lineStart; c < scan; c++) {
|
||||||
|
if (*c == '\n') {
|
||||||
|
lineNum++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Advance to end of line
|
// Advance to end of line
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ static char **sLabels = NULL; // stb_ds array of strdup'd strings
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
static void onPrjWinClose(WindowT *win);
|
static void onPrjWinClose(WindowT *win);
|
||||||
static void onTreeItemClick(WidgetT *w);
|
static void onTreeItemDblClick(WidgetT *w);
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// prjInit
|
// prjInit
|
||||||
|
|
@ -362,7 +362,7 @@ static void onPrjWinClose(WindowT *win) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void onTreeItemClick(WidgetT *w) {
|
static void onTreeItemDblClick(WidgetT *w) {
|
||||||
if (!sPrj || !sOnClick) {
|
if (!sPrj || !sOnClick) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -473,7 +473,7 @@ void prjRebuildTree(PrjStateT *prj) {
|
||||||
arrput(sLabels, label);
|
arrput(sLabels, label);
|
||||||
WidgetT *item = wgtTreeItem(formsNode, label);
|
WidgetT *item = wgtTreeItem(formsNode, label);
|
||||||
item->userData = (void *)(intptr_t)i;
|
item->userData = (void *)(intptr_t)i;
|
||||||
item->onClick = onTreeItemClick;
|
item->onDblClick = onTreeItemDblClick;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -490,7 +490,7 @@ void prjRebuildTree(PrjStateT *prj) {
|
||||||
arrput(sLabels, label);
|
arrput(sLabels, label);
|
||||||
WidgetT *item = wgtTreeItem(modsNode, label);
|
WidgetT *item = wgtTreeItem(modsNode, label);
|
||||||
item->userData = (void *)(intptr_t)i;
|
item->userData = (void *)(intptr_t)i;
|
||||||
item->onClick = onTreeItemClick;
|
item->onDblClick = onTreeItemDblClick;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,7 @@
|
||||||
#define CMD_TILE_H 202
|
#define CMD_TILE_H 202
|
||||||
#define CMD_TILE_V 203
|
#define CMD_TILE_V 203
|
||||||
#define CMD_MIN_ON_RUN 104
|
#define CMD_MIN_ON_RUN 104
|
||||||
|
#define CMD_RESTORE_ALONE 105
|
||||||
#define CMD_ABOUT 300
|
#define CMD_ABOUT 300
|
||||||
#define CMD_TASK_MGR 301
|
#define CMD_TASK_MGR 301
|
||||||
#define CMD_SYSINFO 302
|
#define CMD_SYSINFO 302
|
||||||
|
|
@ -100,6 +101,7 @@ static WindowT *sPmWindow = NULL;
|
||||||
static WidgetT *sStatusLabel = NULL;
|
static WidgetT *sStatusLabel = NULL;
|
||||||
static PrefsHandleT *sPrefs = NULL;
|
static PrefsHandleT *sPrefs = NULL;
|
||||||
static bool sMinOnRun = false;
|
static bool sMinOnRun = false;
|
||||||
|
static bool sRestoreAlone = false;
|
||||||
static AppEntryT sAppFiles[MAX_APP_FILES];
|
static AppEntryT sAppFiles[MAX_APP_FILES];
|
||||||
static int32_t sAppCount = 0;
|
static int32_t sAppCount = 0;
|
||||||
|
|
||||||
|
|
@ -164,10 +166,11 @@ static void buildPmWindow(void) {
|
||||||
MenuT *fileMenu = wmAddMenu(menuBar, "&File");
|
MenuT *fileMenu = wmAddMenu(menuBar, "&File");
|
||||||
wmAddMenuItem(fileMenu, "&Run...", CMD_RUN);
|
wmAddMenuItem(fileMenu, "&Run...", CMD_RUN);
|
||||||
wmAddMenuSeparator(fileMenu);
|
wmAddMenuSeparator(fileMenu);
|
||||||
wmAddMenuItem(fileMenu, "E&xit Shell", CMD_EXIT);
|
wmAddMenuItem(fileMenu, "E&xit DVX", CMD_EXIT);
|
||||||
|
|
||||||
MenuT *optMenu = wmAddMenu(menuBar, "&Options");
|
MenuT *optMenu = wmAddMenu(menuBar, "&Options");
|
||||||
wmAddMenuCheckItem(optMenu, "&Minimize on Run", CMD_MIN_ON_RUN, sMinOnRun);
|
wmAddMenuCheckItem(optMenu, "&Minimize on Run", CMD_MIN_ON_RUN, sMinOnRun);
|
||||||
|
wmAddMenuCheckItem(optMenu, "&Restore when Alone", CMD_RESTORE_ALONE, sRestoreAlone);
|
||||||
|
|
||||||
MenuT *windowMenu = wmAddMenu(menuBar, "&Window");
|
MenuT *windowMenu = wmAddMenu(menuBar, "&Window");
|
||||||
wmAddMenuItem(windowMenu, "&Cascade", CMD_CASCADE);
|
wmAddMenuItem(windowMenu, "&Cascade", CMD_CASCADE);
|
||||||
|
|
@ -176,7 +179,7 @@ static void buildPmWindow(void) {
|
||||||
wmAddMenuItem(windowMenu, "Tile &Vertically", CMD_TILE_V);
|
wmAddMenuItem(windowMenu, "Tile &Vertically", CMD_TILE_V);
|
||||||
|
|
||||||
MenuT *helpMenu = wmAddMenu(menuBar, "&Help");
|
MenuT *helpMenu = wmAddMenu(menuBar, "&Help");
|
||||||
wmAddMenuItem(helpMenu, "&About DVX Shell...", CMD_ABOUT);
|
wmAddMenuItem(helpMenu, "&About DVX...", CMD_ABOUT);
|
||||||
wmAddMenuItem(helpMenu, "&System Information...", CMD_SYSINFO);
|
wmAddMenuItem(helpMenu, "&System Information...", CMD_SYSINFO);
|
||||||
wmAddMenuSeparator(helpMenu);
|
wmAddMenuSeparator(helpMenu);
|
||||||
wmAddMenuItem(helpMenu, "&Task Manager\tCtrl+Esc", CMD_TASK_MGR);
|
wmAddMenuItem(helpMenu, "&Task Manager\tCtrl+Esc", CMD_TASK_MGR);
|
||||||
|
|
@ -262,6 +265,13 @@ static void buildPmWindow(void) {
|
||||||
// (Task Manager refresh is handled by the shell's shellDesktopUpdate.)
|
// (Task Manager refresh is handled by the shell's shellDesktopUpdate.)
|
||||||
static void desktopUpdate(void) {
|
static void desktopUpdate(void) {
|
||||||
updateStatusText();
|
updateStatusText();
|
||||||
|
|
||||||
|
// Auto-restore if we're the only running app and minimized
|
||||||
|
if (sRestoreAlone && sPmWindow && sPmWindow->minimized) {
|
||||||
|
if (shellRunningAppCount() <= 1) {
|
||||||
|
wmRestoreMinimized(&sAc->stack, &sAc->dirty, &sAc->display, sPmWindow);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -288,7 +298,7 @@ static void onAppButtonClick(WidgetT *w) {
|
||||||
// shellTerminateAllApps() to gracefully tear down all loaded DXEs.
|
// shellTerminateAllApps() to gracefully tear down all loaded DXEs.
|
||||||
static void onPmClose(WindowT *win) {
|
static void onPmClose(WindowT *win) {
|
||||||
(void)win;
|
(void)win;
|
||||||
int32_t result = dvxMessageBox(sAc, "Exit Shell", "Are you sure you want to exit DVX Shell?", MB_YESNO | MB_ICONQUESTION);
|
int32_t result = dvxMessageBox(sAc, "Exit DVX", "Are you sure you want to exit DVX?", MB_YESNO | MB_ICONQUESTION);
|
||||||
|
|
||||||
if (result == ID_YES) {
|
if (result == ID_YES) {
|
||||||
dvxQuit(sAc);
|
dvxQuit(sAc);
|
||||||
|
|
@ -346,6 +356,13 @@ static void onPmMenu(WindowT *win, int32_t menuId) {
|
||||||
prefsSave(sPrefs);
|
prefsSave(sPrefs);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case CMD_RESTORE_ALONE:
|
||||||
|
sRestoreAlone = !sRestoreAlone;
|
||||||
|
shellEnsureConfigDir(sCtx);
|
||||||
|
prefsSetBool(sPrefs, "options", "restoreAlone", sRestoreAlone);
|
||||||
|
prefsSave(sPrefs);
|
||||||
|
break;
|
||||||
|
|
||||||
case CMD_ABOUT:
|
case CMD_ABOUT:
|
||||||
showAboutDialog();
|
showAboutDialog();
|
||||||
break;
|
break;
|
||||||
|
|
@ -464,8 +481,8 @@ static void scanAppsDirRecurse(const char *dirPath) {
|
||||||
|
|
||||||
|
|
||||||
static void showAboutDialog(void) {
|
static void showAboutDialog(void) {
|
||||||
dvxMessageBox(sAc, "About DVX Shell",
|
dvxMessageBox(sAc, "About DVX",
|
||||||
"DVX Shell 1.0\nA DOS Visual eXecutive desktop shell for DJGPP/DPMI. Using DXE3 dynamic loading for application modules.",
|
"DVX 1.0 - \"DOS Visual eXecutive\" GUI System.\n\n\"We have Windows at home.\"\n\nCopyright 2026 Scott Duensing\nKangaroo Punch Studios\nhttps://kangaroopunch.com",
|
||||||
MB_OK | MB_ICONINFO);
|
MB_OK | MB_ICONINFO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -538,6 +555,7 @@ int32_t appMain(DxeAppContextT *ctx) {
|
||||||
shellConfigPath(sCtx, "progman.ini", prefsPath, sizeof(prefsPath));
|
shellConfigPath(sCtx, "progman.ini", prefsPath, sizeof(prefsPath));
|
||||||
sPrefs = prefsLoad(prefsPath);
|
sPrefs = prefsLoad(prefsPath);
|
||||||
sMinOnRun = prefsGetBool(sPrefs, "options", "minimizeOnRun", false);
|
sMinOnRun = prefsGetBool(sPrefs, "options", "minimizeOnRun", false);
|
||||||
|
sRestoreAlone = prefsGetBool(sPrefs, "options", "restoreAlone", false);
|
||||||
|
|
||||||
scanAppsDir();
|
scanAppsDir();
|
||||||
buildPmWindow();
|
buildPmWindow();
|
||||||
|
|
|
||||||
|
|
@ -1773,12 +1773,7 @@ static void handleMouseButton(AppContextT *ctx, int32_t mx, int32_t my, int32_t
|
||||||
if (ctx->lastIconClickId == iconWin->id &&
|
if (ctx->lastIconClickId == iconWin->id &&
|
||||||
(now - ctx->lastIconClickTime) < ctx->dblClickTicks) {
|
(now - ctx->lastIconClickTime) < ctx->dblClickTicks) {
|
||||||
// Double-click: restore minimized window
|
// Double-click: restore minimized window
|
||||||
// Dirty the entire icon area (may span multiple rows)
|
wmRestoreMinimized(&ctx->stack, &ctx->dirty, &ctx->display, iconWin);
|
||||||
int32_t iconY;
|
|
||||||
int32_t iconH;
|
|
||||||
wmMinimizedIconRect(&ctx->stack, &ctx->display, &iconY, &iconH);
|
|
||||||
dirtyListAdd(&ctx->dirty, 0, iconY, ctx->display.width, iconH);
|
|
||||||
wmRestoreMinimized(&ctx->stack, &ctx->dirty, iconWin);
|
|
||||||
ctx->lastIconClickId = -1;
|
ctx->lastIconClickId = -1;
|
||||||
} else {
|
} else {
|
||||||
// First click -- record for double-click detection
|
// First click -- record for double-click detection
|
||||||
|
|
|
||||||
|
|
@ -575,6 +575,12 @@ static void wordWrapDraw(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *fo
|
||||||
drawTextN(d, ops, font, x, curY, text, lineLen, fg, bg, true);
|
drawTextN(d, ops, font, x, curY, text, lineLen, fg, bg, true);
|
||||||
curY += lineH;
|
curY += lineH;
|
||||||
text += lineLen;
|
text += lineLen;
|
||||||
|
|
||||||
|
// Skip the newline that ended this line (if any) so the
|
||||||
|
// explicit \n handler at the top doesn't double-advance.
|
||||||
|
if (*text == '\n') {
|
||||||
|
text++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -631,6 +637,10 @@ static int32_t wordWrapHeight(const BitmapFontT *font, const char *text, int32_t
|
||||||
|
|
||||||
lines++;
|
lines++;
|
||||||
text += lineLen;
|
text += lineLen;
|
||||||
|
|
||||||
|
if (*text == '\n') {
|
||||||
|
text++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lines == 0) {
|
if (lines == 0) {
|
||||||
|
|
|
||||||
20
core/dvxWm.c
20
core/dvxWm.c
|
|
@ -2403,21 +2403,21 @@ void wmRestore(WindowStackT *stack, DirtyListT *dl, const DisplayT *d, WindowT *
|
||||||
// wmRestoreMinimized
|
// wmRestoreMinimized
|
||||||
// ============================================================
|
// ============================================================
|
||||||
//
|
//
|
||||||
// Restores a minimized window: clears the minimized flag, raises the window
|
// Restores a minimized window: clears the minimized flag, dirties the
|
||||||
// to the top of the z-order, and gives it focus. No content buffer
|
// icon strip so the old icon is erased, raises the window to the top
|
||||||
// reallocation is needed because the buffer was preserved while minimized.
|
// of the z-order, and gives it focus.
|
||||||
//
|
|
||||||
// The icon strip area is implicitly dirtied by the raise/focus operations
|
|
||||||
// and by the window area dirty at the end. The compositor repaints the
|
|
||||||
// entire icon strip on any dirty rect that intersects it, so icon positions
|
|
||||||
// are always correct after restore even though we don't dirty the icon
|
|
||||||
// strip explicitly.
|
|
||||||
|
|
||||||
void wmRestoreMinimized(WindowStackT *stack, DirtyListT *dl, WindowT *win) {
|
void wmRestoreMinimized(WindowStackT *stack, DirtyListT *dl, const DisplayT *d, WindowT *win) {
|
||||||
if (!win->minimized) {
|
if (!win->minimized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dirty the icon strip area so the old minimized icon is erased
|
||||||
|
int32_t iconY;
|
||||||
|
int32_t iconH;
|
||||||
|
wmMinimizedIconRect(stack, d, &iconY, &iconH);
|
||||||
|
dirtyListAdd(dl, 0, iconY, d->width, iconH);
|
||||||
|
|
||||||
win->minimized = false;
|
win->minimized = false;
|
||||||
|
|
||||||
for (int32_t i = 0; i < stack->count; i++) {
|
for (int32_t i = 0; i < stack->count; i++) {
|
||||||
|
|
|
||||||
|
|
@ -200,7 +200,7 @@ void wmMinimizedIconRect(const WindowStackT *stack, const DisplayT *d, int32_t *
|
||||||
void wmRestore(WindowStackT *stack, DirtyListT *dl, const DisplayT *d, WindowT *win);
|
void wmRestore(WindowStackT *stack, DirtyListT *dl, const DisplayT *d, WindowT *win);
|
||||||
|
|
||||||
// Restore a minimized window
|
// Restore a minimized window
|
||||||
void wmRestoreMinimized(WindowStackT *stack, DirtyListT *dl, WindowT *win);
|
void wmRestoreMinimized(WindowStackT *stack, DirtyListT *dl, const DisplayT *d, WindowT *win);
|
||||||
|
|
||||||
// Load an icon image for a window (converts to display pixel format)
|
// Load an icon image for a window (converts to display pixel format)
|
||||||
int32_t wmSetIcon(WindowT *win, const char *path, const DisplayT *d);
|
int32_t wmSetIcon(WindowT *win, const char *path, const DisplayT *d);
|
||||||
|
|
|
||||||
|
|
@ -178,7 +178,7 @@ static void onTmSwitchTo(WidgetT *w) {
|
||||||
|
|
||||||
if (win->appId == app->appId) {
|
if (win->appId == app->appId) {
|
||||||
if (win->minimized) {
|
if (win->minimized) {
|
||||||
wmRestoreMinimized(&sCtx->stack, &sCtx->dirty, win);
|
wmRestoreMinimized(&sCtx->stack, &sCtx->dirty, &sCtx->display, win);
|
||||||
}
|
}
|
||||||
|
|
||||||
wmRaiseWindow(&sCtx->stack, &sCtx->dirty, j);
|
wmRaiseWindow(&sCtx->stack, &sCtx->dirty, j);
|
||||||
|
|
|
||||||
|
|
@ -1192,12 +1192,20 @@ void widgetTreeViewOnMouse(WidgetT *hit, WidgetT *root, int32_t vx, int32_t vy)
|
||||||
item->onChange(item);
|
item->onChange(item);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (item->onClick) {
|
int32_t clicks = multiClickDetect(vx, vy);
|
||||||
|
|
||||||
|
if (clicks >= 2 && item->onDblClick) {
|
||||||
|
item->onDblClick(item);
|
||||||
|
} else if (item->onClick) {
|
||||||
item->onClick(item);
|
item->onClick(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (item->onClick) {
|
int32_t clicks = multiClickDetect(vx, vy);
|
||||||
|
|
||||||
|
if (clicks >= 2 && item->onDblClick) {
|
||||||
|
item->onDblClick(item);
|
||||||
|
} else if (item->onClick) {
|
||||||
item->onClick(item);
|
item->onClick(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue