DVX_GUI/apps/ctrlpanel/ctrlpanel.c

806 lines
23 KiB
C

// ctrlpanel.c — DVX Control Panel
//
// System configuration app with four tabs:
// Mouse — scroll direction, double-click speed, acceleration
// Colors — all 18 system colors, theme load/save
// Desktop — wallpaper image (stretch mode)
// Video — resolution and color depth
//
// Changes preview live. OK saves to DVX.INI, Cancel reverts to the
// state captured when the control panel was opened.
#include "dvxApp.h"
#include "dvxDialog.h"
#include "dvxPrefs.h"
#include "dvxWidget.h"
#include "dvxWm.h"
#include "platform/dvxPlatform.h"
#include "shellApp.h"
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <time.h>
// ============================================================
// Constants
// ============================================================
#define CP_WIN_W 460
#define CP_WIN_H 340
#define MAX_VIDEO_MODES 64
#define MAX_THEME_FILES 32
#define THEME_DIR "CONFIG/THEMES"
#define THEME_EXT ".THM"
// ============================================================
// App descriptor
// ============================================================
AppDescriptorT appDescriptor = {
.name = "Control Panel",
.hasMainLoop = false,
.multiInstance = false,
.stackSize = SHELL_STACK_DEFAULT,
.priority = TS_PRIORITY_NORMAL
};
// ============================================================
// Video mode entry
// ============================================================
typedef struct {
int32_t w;
int32_t h;
int32_t bpp;
char label[32];
} VideoModeEntryT;
// ============================================================
// Module state
// ============================================================
static DxeAppContextT *sCtx = NULL;
static AppContextT *sAc = NULL;
static WindowT *sWin = NULL;
// Saved state for Cancel
static uint8_t sSavedColorRgb[ColorCountE][3];
static int32_t sSavedWheelDir;
static int32_t sSavedDblClick;
static int32_t sSavedAccel;
static int32_t sSavedVideoW;
static int32_t sSavedVideoH;
static int32_t sSavedVideoBpp;
static bool sSavedHadWallpaper;
// Mouse tab widgets
static WidgetT *sWheelDrop = NULL;
static WidgetT *sDblClickSldr = NULL;
static WidgetT *sDblClickLbl = NULL;
static WidgetT *sAccelDrop = NULL;
// Colors tab widgets
static WidgetT *sColorList = NULL;
static WidgetT *sRedSldr = NULL;
static WidgetT *sGreenSldr = NULL;
static WidgetT *sBlueSldr = NULL;
static WidgetT *sRedLbl = NULL;
static WidgetT *sGreenLbl = NULL;
static WidgetT *sBlueLbl = NULL;
// Desktop tab widgets
static WidgetT *sWallpaperLbl = NULL;
static char sWallpaperPath[260];
// Video tab
static VideoModeEntryT sVideoModes[MAX_VIDEO_MODES];
static const char *sVideoLabels[MAX_VIDEO_MODES];
static int32_t sVideoModeCount = 0;
static WidgetT *sVideoList = NULL;
// Theme list
static char sThemeNames[MAX_THEME_FILES][64];
static const char *sThemeLabels[MAX_THEME_FILES];
static char sThemePaths[MAX_THEME_FILES][260];
static int32_t sThemeCount = 0;
static WidgetT *sThemeList = NULL;
// ============================================================
// Prototypes
// ============================================================
int32_t appMain(DxeAppContextT *ctx);
static void buildMouseTab(WidgetT *page);
static void buildColorsTab(WidgetT *page);
static void buildDesktopTab(WidgetT *page);
static void buildVideoTab(WidgetT *page);
static void enumVideoModesCb(int32_t w, int32_t h, int32_t bpp, void *userData);
static int32_t mapAccelName(const char *name);
static const char *mapAccelValue(int32_t val);
static void onAccelChange(WidgetT *w);
static void onApplyTheme(WidgetT *w);
static void onResetColors(WidgetT *w);
static void onCancel(WidgetT *w);
static void onChooseWallpaper(WidgetT *w);
static void onClearWallpaper(WidgetT *w);
static void onClose(WindowT *win);
static void onColorSelect(WidgetT *w);
static void onColorSlider(WidgetT *w);
static void onDblClickSlider(WidgetT *w);
static void onOk(WidgetT *w);
static void onSaveTheme(WidgetT *w);
static void onVideoApply(WidgetT *w);
static void onWheelChange(WidgetT *w);
static void saveSnapshot(void);
static void restoreSnapshot(void);
static void scanThemes(void);
static void updateColorSliders(void);
static void updateDblClickLabel(void);
// ============================================================
// buildColorsTab
// ============================================================
static void buildColorsTab(WidgetT *page) {
// Color list on the left, sliders on the right
WidgetT *hbox = wgtHBox(page);
hbox->weight = 100;
hbox->spacing = wgtPixels(8);
// Left side: color name list + theme controls
WidgetT *leftVbox = wgtVBox(hbox);
leftVbox->weight = 50;
leftVbox->spacing = wgtPixels(4);
wgtLabel(leftVbox, "System Colors:");
// Build color name list
static const char *colorNames[ColorCountE];
for (int32_t i = 0; i < ColorCountE; i++) {
colorNames[i] = dvxColorName((ColorIdE)i);
}
sColorList = wgtListBox(leftVbox);
sColorList->weight = 100;
sColorList->onChange = onColorSelect;
wgtListBoxSetItems(sColorList, colorNames, ColorCountE);
wgtListBoxSetSelected(sColorList, 0);
// Theme row
WidgetT *themeRow = wgtHBox(leftVbox);
themeRow->spacing = wgtPixels(4);
wgtLabel(themeRow, "Theme:");
scanThemes();
sThemeList = wgtDropdown(themeRow);
sThemeList->weight = 100;
wgtDropdownSetItems(sThemeList, sThemeLabels, sThemeCount);
WidgetT *loadBtn = wgtButton(themeRow, "Apply");
loadBtn->onClick = onApplyTheme;
loadBtn->prefW = wgtPixels(60);
WidgetT *saveBtn = wgtButton(themeRow, "Save...");
saveBtn->onClick = onSaveTheme;
saveBtn->prefW = wgtPixels(60);
WidgetT *resetBtn = wgtButton(themeRow, "Reset");
resetBtn->onClick = onResetColors;
resetBtn->prefW = wgtPixels(60);
// Right side: RGB sliders
WidgetT *rightVbox = wgtVBox(hbox);
rightVbox->weight = 50;
rightVbox->spacing = wgtPixels(4);
wgtLabel(rightVbox, "Red:");
sRedSldr = wgtSlider(rightVbox, 0, 255);
sRedSldr->onChange = onColorSlider;
sRedLbl = wgtLabel(rightVbox, "0");
wgtLabel(rightVbox, "Green:");
sGreenSldr = wgtSlider(rightVbox, 0, 255);
sGreenSldr->onChange = onColorSlider;
sGreenLbl = wgtLabel(rightVbox, "0");
wgtLabel(rightVbox, "Blue:");
sBlueSldr = wgtSlider(rightVbox, 0, 255);
sBlueSldr->onChange = onColorSlider;
sBlueLbl = wgtLabel(rightVbox, "0");
updateColorSliders();
}
// ============================================================
// buildDesktopTab
// ============================================================
static void buildDesktopTab(WidgetT *page) {
wgtLabel(page, "Wallpaper:");
sWallpaperLbl = wgtLabel(page, sWallpaperPath[0] ? sWallpaperPath : "(none)");
WidgetT *btnRow = wgtHBox(page);
btnRow->spacing = wgtPixels(8);
WidgetT *chooseBtn = wgtButton(btnRow, "Browse...");
chooseBtn->onClick = onChooseWallpaper;
chooseBtn->prefW = wgtPixels(90);
WidgetT *clearBtn = wgtButton(btnRow, "Clear");
clearBtn->onClick = onClearWallpaper;
clearBtn->prefW = wgtPixels(90);
}
// ============================================================
// buildMouseTab
// ============================================================
static void buildMouseTab(WidgetT *page) {
// Scroll direction
WidgetT *wheelRow = wgtHBox(page);
wheelRow->spacing = wgtPixels(8);
wgtLabel(wheelRow, "Scroll Wheel:");
static const char *wheelItems[] = {"Normal", "Reversed"};
sWheelDrop = wgtDropdown(wheelRow);
sWheelDrop->weight = 100;
sWheelDrop->onChange = onWheelChange;
wgtDropdownSetItems(sWheelDrop, wheelItems, 2);
wgtDropdownSetSelected(sWheelDrop, sAc->wheelDirection < 0 ? 1 : 0);
// Double-click speed
wgtLabel(page, "Double-Click Speed:");
WidgetT *dblRow = wgtHBox(page);
dblRow->spacing = wgtPixels(8);
wgtLabel(dblRow, "Fast");
sDblClickSldr = wgtSlider(dblRow, 200, 900);
sDblClickSldr->weight = 100;
sDblClickSldr->onChange = onDblClickSlider;
int32_t dblMs = prefsGetInt("mouse", "doubleclick", 500);
wgtSliderSetValue(sDblClickSldr, dblMs);
wgtLabel(dblRow, "Slow");
sDblClickLbl = wgtLabel(page, "");
updateDblClickLabel();
// Acceleration
WidgetT *accelRow = wgtHBox(page);
accelRow->spacing = wgtPixels(8);
wgtLabel(accelRow, "Acceleration:");
static const char *accelItems[] = {"Off", "Low", "Medium", "High"};
sAccelDrop = wgtDropdown(accelRow);
sAccelDrop->weight = 100;
sAccelDrop->onChange = onAccelChange;
wgtDropdownSetItems(sAccelDrop, accelItems, 4);
const char *accelStr = prefsGetString("mouse", "acceleration", "medium");
if (strcmp(accelStr, "off") == 0) {
wgtDropdownSetSelected(sAccelDrop, 0);
} else if (strcmp(accelStr, "low") == 0) {
wgtDropdownSetSelected(sAccelDrop, 1);
} else if (strcmp(accelStr, "high") == 0) {
wgtDropdownSetSelected(sAccelDrop, 3);
} else {
wgtDropdownSetSelected(sAccelDrop, 2);
}
}
// ============================================================
// buildVideoTab
// ============================================================
static void buildVideoTab(WidgetT *page) {
wgtLabel(page, "Available Video Modes:");
sVideoModeCount = 0;
platformVideoEnumModes(enumVideoModesCb, NULL);
sVideoList = wgtListBox(page);
sVideoList->weight = 100;
wgtListBoxSetItems(sVideoList, sVideoLabels, sVideoModeCount);
// Select the current mode
for (int32_t i = 0; i < sVideoModeCount; i++) {
if (sVideoModes[i].w == sAc->display.width &&
sVideoModes[i].h == sAc->display.height &&
sVideoModes[i].bpp == sAc->display.format.bitsPerPixel) {
wgtListBoxSetSelected(sVideoList, i);
break;
}
}
WidgetT *applyBtn = wgtButton(page, "Apply Mode");
applyBtn->onClick = onVideoApply;
applyBtn->prefW = wgtPixels(100);
}
// ============================================================
// enumVideoModesCb
// ============================================================
static void enumVideoModesCb(int32_t w, int32_t h, int32_t bpp, void *userData) {
(void)userData;
if (sVideoModeCount >= MAX_VIDEO_MODES) {
return;
}
VideoModeEntryT *m = &sVideoModes[sVideoModeCount];
m->w = w;
m->h = h;
m->bpp = bpp;
snprintf(m->label, sizeof(m->label), "%ldx%ld %ldbpp", (long)w, (long)h, (long)bpp);
sVideoLabels[sVideoModeCount] = m->label;
sVideoModeCount++;
}
// ============================================================
// mapAccelName / mapAccelValue
// ============================================================
static int32_t mapAccelName(const char *name) {
if (strcmp(name, "off") == 0) return 10000;
if (strcmp(name, "low") == 0) return 100;
if (strcmp(name, "medium") == 0) return 64;
if (strcmp(name, "high") == 0) return 32;
return 64;
}
static const char *mapAccelValue(int32_t idx) {
switch (idx) {
case 0: return "off";
case 1: return "low";
case 2: return "medium";
case 3: return "high";
default: return "medium";
}
}
// ============================================================
// Callbacks — Mouse tab
// ============================================================
static void onWheelChange(WidgetT *w) {
int32_t sel = wgtDropdownGetSelected(w);
int32_t dir = (sel == 1) ? -1 : 1;
int32_t dbl = wgtSliderGetValue(sDblClickSldr);
const char *accelName = mapAccelValue(wgtDropdownGetSelected(sAccelDrop));
int32_t accelVal = mapAccelName(accelName);
dvxSetMouseConfig(sAc, dir, dbl, accelVal);
}
static void onDblClickSlider(WidgetT *w) {
(void)w;
updateDblClickLabel();
int32_t dir = (wgtDropdownGetSelected(sWheelDrop) == 1) ? -1 : 1;
int32_t dbl = wgtSliderGetValue(sDblClickSldr);
const char *accelName = mapAccelValue(wgtDropdownGetSelected(sAccelDrop));
int32_t accelVal = mapAccelName(accelName);
dvxSetMouseConfig(sAc, dir, dbl, accelVal);
}
static void onAccelChange(WidgetT *w) {
int32_t dir = (wgtDropdownGetSelected(sWheelDrop) == 1) ? -1 : 1;
int32_t dbl = wgtSliderGetValue(sDblClickSldr);
const char *accelName = mapAccelValue(wgtDropdownGetSelected(w));
int32_t accelVal = mapAccelName(accelName);
dvxSetMouseConfig(sAc, dir, dbl, accelVal);
}
// ============================================================
// Callbacks — Colors tab
// ============================================================
static void onColorSelect(WidgetT *w) {
(void)w;
updateColorSliders();
}
static void onColorSlider(WidgetT *w) {
(void)w;
int32_t sel = wgtListBoxGetSelected(sColorList);
if (sel < 0 || sel >= ColorCountE) {
return;
}
uint8_t r = (uint8_t)wgtSliderGetValue(sRedSldr);
uint8_t g = (uint8_t)wgtSliderGetValue(sGreenSldr);
uint8_t b = (uint8_t)wgtSliderGetValue(sBlueSldr);
// Update label text
static char rBuf[8];
static char gBuf[8];
static char bBuf[8];
snprintf(rBuf, sizeof(rBuf), "%d", r);
snprintf(gBuf, sizeof(gBuf), "%d", g);
snprintf(bBuf, sizeof(bBuf), "%d", b);
wgtSetText(sRedLbl, rBuf);
wgtSetText(sGreenLbl, gBuf);
wgtSetText(sBlueLbl, bBuf);
dvxSetColor(sAc, (ColorIdE)sel, r, g, b);
}
static void onApplyTheme(WidgetT *w) {
(void)w;
int32_t sel = wgtDropdownGetSelected(sThemeList);
if (sel < 0 || sel >= sThemeCount) {
return;
}
dvxLoadTheme(sAc, sThemePaths[sel]);
updateColorSliders();
}
static void onSaveTheme(WidgetT *w) {
(void)w;
FileFilterT filters[] = {
{ "Theme Files (*.thm)", "*.thm" },
{ "All Files (*.*)", "*.*" }
};
char path[260];
if (dvxFileDialog(sAc, "Save Theme", FD_SAVE, THEME_DIR, filters, 2, path, sizeof(path))) {
dvxSaveTheme(sAc, path);
scanThemes();
wgtDropdownSetItems(sThemeList, sThemeLabels, sThemeCount);
}
}
static void onResetColors(WidgetT *w) {
(void)w;
dvxResetColorScheme(sAc);
updateColorSliders();
}
// ============================================================
// Callbacks — Desktop tab
// ============================================================
static void onChooseWallpaper(WidgetT *w) {
(void)w;
FileFilterT filters[] = {
{ "Images (*.bmp;*.png;*.jpg)", "*.bmp" },
{ "All Files (*.*)", "*.*" }
};
char path[260];
if (dvxFileDialog(sAc, "Choose Wallpaper", FD_OPEN, NULL, filters, 2, path, sizeof(path))) {
if (dvxSetWallpaper(sAc, path)) {
strncpy(sWallpaperPath, path, sizeof(sWallpaperPath) - 1);
sWallpaperPath[sizeof(sWallpaperPath) - 1] = '\0';
wgtSetText(sWallpaperLbl, sWallpaperPath);
} else {
dvxMessageBox(sAc, "Error", "Could not load wallpaper image.", MB_OK | MB_ICONERROR);
}
}
}
static void onClearWallpaper(WidgetT *w) {
(void)w;
dvxSetWallpaper(sAc, NULL);
sWallpaperPath[0] = '\0';
wgtSetText(sWallpaperLbl, "(none)");
}
// ============================================================
// Callbacks — Video tab
// ============================================================
static void onVideoApply(WidgetT *w) {
(void)w;
int32_t sel = wgtListBoxGetSelected(sVideoList);
if (sel < 0 || sel >= sVideoModeCount) {
return;
}
VideoModeEntryT *m = &sVideoModes[sel];
if (m->w == sAc->display.width &&
m->h == sAc->display.height &&
m->bpp == sAc->display.format.bitsPerPixel) {
return;
}
if (dvxChangeVideoMode(sAc, m->w, m->h, m->bpp) != 0) {
dvxMessageBox(sAc, "Error", "Failed to change video mode.", MB_OK | MB_ICONERROR);
}
}
// ============================================================
// Callbacks — OK / Cancel / Close
// ============================================================
static void onOk(WidgetT *w) {
(void)w;
// Save mouse settings
int32_t wheelSel = wgtDropdownGetSelected(sWheelDrop);
prefsSetString("mouse", "wheel", wheelSel == 1 ? "reversed" : "normal");
prefsSetInt("mouse", "doubleclick", wgtSliderGetValue(sDblClickSldr));
prefsSetString("mouse", "acceleration", mapAccelValue(wgtDropdownGetSelected(sAccelDrop)));
// Save colors to INI
for (int32_t i = 0; i < ColorCountE; i++) {
uint8_t r;
uint8_t g;
uint8_t b;
dvxGetColor(sAc, (ColorIdE)i, &r, &g, &b);
char val[16];
snprintf(val, sizeof(val), "%d,%d,%d", r, g, b);
prefsSetString("colors", dvxColorName((ColorIdE)i), val);
}
// Save desktop settings
if (sWallpaperPath[0]) {
prefsSetString("desktop", "wallpaper", sWallpaperPath);
} else {
prefsRemove("desktop", "wallpaper");
}
// Save video settings
prefsSetInt("video", "width", sAc->display.width);
prefsSetInt("video", "height", sAc->display.height);
prefsSetInt("video", "bpp", sAc->display.format.bitsPerPixel);
prefsSave();
dvxDestroyWindow(sAc, sWin);
sWin = NULL;
}
static void onCancel(WidgetT *w) {
(void)w;
restoreSnapshot();
dvxDestroyWindow(sAc, sWin);
sWin = NULL;
}
static void onClose(WindowT *win) {
restoreSnapshot();
dvxDestroyWindow(sAc, win);
sWin = NULL;
}
// ============================================================
// saveSnapshot / restoreSnapshot
// ============================================================
static void saveSnapshot(void) {
memcpy(sSavedColorRgb, sAc->colorRgb, sizeof(sSavedColorRgb));
sSavedWheelDir = sAc->wheelDirection;
sSavedDblClick = (int32_t)(sAc->dblClickTicks * 1000 / CLOCKS_PER_SEC);
sSavedAccel = 0;
sSavedVideoW = sAc->display.width;
sSavedVideoH = sAc->display.height;
sSavedVideoBpp = sAc->display.format.bitsPerPixel;
sSavedHadWallpaper = (sAc->wallpaperBuf != NULL);
const char *wp = prefsGetString("desktop", "wallpaper", NULL);
if (wp) {
strncpy(sWallpaperPath, wp, sizeof(sWallpaperPath) - 1);
sWallpaperPath[sizeof(sWallpaperPath) - 1] = '\0';
} else {
sWallpaperPath[0] = '\0';
}
}
static void restoreSnapshot(void) {
// Restore colors
memcpy(sAc->colorRgb, sSavedColorRgb, sizeof(sSavedColorRgb));
dvxApplyColorScheme(sAc);
// Restore mouse
const char *accelStr = prefsGetString("mouse", "acceleration", "medium");
int32_t accelVal = mapAccelName(accelStr);
dvxSetMouseConfig(sAc, sSavedWheelDir, sSavedDblClick, accelVal);
// Restore video mode if changed
if (sAc->display.width != sSavedVideoW ||
sAc->display.height != sSavedVideoH ||
sAc->display.format.bitsPerPixel != sSavedVideoBpp) {
dvxChangeVideoMode(sAc, sSavedVideoW, sSavedVideoH, sSavedVideoBpp);
}
// Restore wallpaper
if (sSavedHadWallpaper && sWallpaperPath[0]) {
dvxSetWallpaper(sAc, sWallpaperPath);
} else {
dvxSetWallpaper(sAc, NULL);
}
}
// ============================================================
// scanThemes
// ============================================================
static void scanThemes(void) {
sThemeCount = 0;
// Scan THEME_DIR for .THM files
DIR *dir = opendir(THEME_DIR);
if (!dir) {
return;
}
struct dirent *ent;
while ((ent = readdir(dir)) != NULL && sThemeCount < MAX_THEME_FILES) {
char *dot = strrchr(ent->d_name, '.');
if (!dot) {
continue;
}
// Case-insensitive extension check
if (strcasecmp(dot, THEME_EXT) != 0) {
continue;
}
// Use filename without extension as display name
int32_t nameLen = (int32_t)(dot - ent->d_name);
if (nameLen >= 64) {
nameLen = 63;
}
memcpy(sThemeNames[sThemeCount], ent->d_name, nameLen);
sThemeNames[sThemeCount][nameLen] = '\0';
sThemeLabels[sThemeCount] = sThemeNames[sThemeCount];
snprintf(sThemePaths[sThemeCount], sizeof(sThemePaths[sThemeCount]), "%s/%s", THEME_DIR, ent->d_name);
sThemeCount++;
}
closedir(dir);
}
// ============================================================
// updateColorSliders
// ============================================================
static void updateColorSliders(void) {
int32_t sel = wgtListBoxGetSelected(sColorList);
if (sel < 0 || sel >= ColorCountE) {
return;
}
uint8_t r;
uint8_t g;
uint8_t b;
dvxGetColor(sAc, (ColorIdE)sel, &r, &g, &b);
wgtSliderSetValue(sRedSldr, r);
wgtSliderSetValue(sGreenSldr, g);
wgtSliderSetValue(sBlueSldr, b);
static char rBuf[8];
static char gBuf[8];
static char bBuf[8];
snprintf(rBuf, sizeof(rBuf), "%d", r);
snprintf(gBuf, sizeof(gBuf), "%d", g);
snprintf(bBuf, sizeof(bBuf), "%d", b);
wgtSetText(sRedLbl, rBuf);
wgtSetText(sGreenLbl, gBuf);
wgtSetText(sBlueLbl, bBuf);
}
// ============================================================
// updateDblClickLabel
// ============================================================
static void updateDblClickLabel(void) {
static char buf[32];
snprintf(buf, sizeof(buf), "%ld ms", (long)wgtSliderGetValue(sDblClickSldr));
wgtSetText(sDblClickLbl, buf);
}
// ============================================================
// appMain
// ============================================================
int32_t appMain(DxeAppContextT *ctx) {
sCtx = ctx;
sAc = ctx->shellCtx;
int32_t winX = (sAc->display.width - CP_WIN_W) / 2;
int32_t winY = (sAc->display.height - CP_WIN_H) / 2;
sWin = dvxCreateWindow(sAc, "Control Panel", winX, winY, CP_WIN_W, CP_WIN_H, true);
if (!sWin) {
return -1;
}
sWin->onClose = onClose;
saveSnapshot();
WidgetT *root = wgtInitWindow(sAc, sWin);
// Tab control fills the window
WidgetT *tabs = wgtTabControl(root);
tabs->weight = 100;
WidgetT *mouseTab = wgtTabPage(tabs, "Mouse");
WidgetT *colorsTab = wgtTabPage(tabs, "Colors");
WidgetT *desktopTab = wgtTabPage(tabs, "Desktop");
WidgetT *videoTab = wgtTabPage(tabs, "Video");
buildMouseTab(mouseTab);
buildColorsTab(colorsTab);
buildDesktopTab(desktopTab);
buildVideoTab(videoTab);
// OK / Cancel buttons at bottom
WidgetT *btnRow = wgtHBox(root);
btnRow->align = AlignEndE;
btnRow->spacing = wgtPixels(8);
WidgetT *okBtn = wgtButton(btnRow, "OK");
okBtn->onClick = onOk;
okBtn->prefW = wgtPixels(80);
WidgetT *cancelBtn = wgtButton(btnRow, "Cancel");
cancelBtn->onClick = onCancel;
cancelBtn->prefW = wgtPixels(80);
dvxFitWindow(sAc, sWin);
return 0;
}