DVX_GUI/src/libs/kpunch/print/printBas.c
2026-10-13 13:49:12 -05:00

473 lines
16 KiB
C

// The MIT License (MIT)
//
// Copyright (C) 2026 Scott Duensing
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
// printBas.c -- DVX BASIC entry points of the printing library
//
// Called through DECLARE LIBRARY (src/include/basic/print.bas), so the
// signatures use only BASIC-marshalable types: strings in, LONG out, True
// (-1) / False (0) for success. The dialogs need the GUI context, which
// the BASIC runtime hands over with printBasSetContext when it starts a
// program; without it (a program run without the GUI) nothing prints.
//
// LPRINT and OPEN "LPT1:" are handled by the BASIC runtime itself, through
// printUiOpenPort / printJobWrite / printJobClose.
//
// The printer settings functions (PrintModel*, PrintDest*, PrintSetting*,
// PrintSettingsProblem$, PrintSettingsStore) take a prefs.bas handle, so a
// program can edit DVX.INI [printer] the way the Control Panel does.
#include "printInt.h"
#include "dvxPrefs.h"
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#define BAS_TRUE (-1)
#define BAS_FALSE 0
#define BAS_NONE (-1)
#define BAS_LPT_FIRST 1 // BASIC numbers the printer ports LPT1 = 1
// A number field of PrintPageSetupT by name (PrintPageSetting)
typedef struct {
const char *name;
size_t offset;
} BasPageFieldT;
static const BasPageFieldT sPageFields[] = {
{ "Lines", offsetof(PrintPageSetupT, linesPerPage) },
{ "Columns", offsetof(PrintPageSetupT, columns) },
{ "MarginTop", offsetof(PrintPageSetupT, marginTop) },
{ "MarginBottom", offsetof(PrintPageSetupT, marginBottom) },
{ "MarginLeft", offsetof(PrintPageSetupT, marginLeft) },
{ "MarginRight", offsetof(PrintPageSetupT, marginRight) },
{ "TabWidth", offsetof(PrintPageSetupT, tabWidth) }
};
#define BAS_PAGE_FIELD_COUNT ((int32_t)(sizeof(sPageFields) / sizeof(sPageFields[0])))
#define BAS_PAGE_WRAP "Wrap"
#define BAS_PAGE_HEADER "Header"
#define BAS_PAGE_FOOTER "Footer"
static AppContextT *sBasCtx = NULL;
static PrintChoiceT sChoice; // the last PrintChoose answer
static char sText[PRINT_MSG_MAX];
// ============================================================
// Prototypes
// ============================================================
static int32_t basBool(bool value);
static bool destValid(int32_t dest);
static bool modelValid(int32_t model);
static void pageSetupFrom(PrintPageSetupT *ps, int32_t lines, int32_t columns, int32_t marginTop, int32_t marginBottom, int32_t marginLeft, int32_t marginRight, int32_t tabWidth, int32_t wrap, const char *header, const char *footer);
void printBasSetContext(AppContextT *ctx);
int32_t PrintCanPrintPictures(void);
int32_t PrintChoiceCopies(void);
int32_t PrintChoiceDest(void);
const char *PrintChoiceFile(void);
int32_t PrintChoiceFirst(void);
int32_t PrintChoiceLast(void);
int32_t PrintChoose(const char *title, int32_t pageCount);
int32_t PrintDestCount(void);
int32_t PrintDestIsFile(int32_t dest);
const char *PrintDestLabel(int32_t dest);
int32_t PrintModelCount(void);
const char *PrintModelLabel(int32_t model);
int32_t PrintPageCount(const char *text);
int32_t PrintPageLines(int32_t prefs, int32_t model);
int32_t PrintPageSetting(int32_t prefs, const char *field);
int32_t PrintPageSettingsStore(int32_t prefs, int32_t lines, int32_t columns, int32_t marginTop, int32_t marginBottom, int32_t marginLeft, int32_t marginRight, int32_t tabWidth, int32_t wrap, const char *header, const char *footer);
int32_t PrintPageSetup(void);
const char *PrintPageSetupProblem(int32_t lines, int32_t columns, int32_t marginTop, int32_t marginBottom, int32_t marginLeft, int32_t marginRight, int32_t tabWidth, int32_t wrap, const char *header, const char *footer);
const char *PrintPageText(int32_t prefs, const char *field);
int32_t PrintPicture(const char *path, const char *title);
int32_t PrintPictureDialog(const char *path, const char *title);
int32_t PrintPortPresent(int32_t port);
int32_t PrintPortStatus(int32_t port);
int32_t PrintSettingDest(int32_t prefs);
const char *PrintSettingFile(int32_t prefs);
int32_t PrintSettingModel(int32_t prefs);
const char *PrintSettingsProblem(int32_t dest, const char *filePath);
int32_t PrintSettingsStore(int32_t prefs, int32_t model, int32_t dest, const char *filePath);
const char *PrintStatusProblem(int32_t status, int32_t afterWrite, const char *where);
int32_t PrintText(const char *text, const char *title);
int32_t PrintTextDialog(const char *text, const char *title);
int32_t PrintTextPages(const char *text, const char *title, int32_t firstPage, int32_t lastPage, int32_t copies);
static int32_t basBool(bool value) {
return value ? BAS_TRUE : BAS_FALSE;
}
static bool destValid(int32_t dest) {
return dest >= 0 && dest < PrintDestCountE;
}
static bool modelValid(int32_t model) {
return model >= 0 && model < PrintModelCountE;
}
// A page setup from PrintPageSettingsStore's arguments (texts too long
// are cut off).
static void pageSetupFrom(PrintPageSetupT *ps, int32_t lines, int32_t columns, int32_t marginTop, int32_t marginBottom, int32_t marginLeft, int32_t marginRight, int32_t tabWidth, int32_t wrap, const char *header, const char *footer) {
memset(ps, 0, sizeof(*ps));
ps->linesPerPage = lines;
ps->columns = columns;
ps->marginTop = marginTop;
ps->marginBottom = marginBottom;
ps->marginLeft = marginLeft;
ps->marginRight = marginRight;
ps->tabWidth = tabWidth;
ps->wrap = wrap != 0;
snprintf(ps->header, sizeof(ps->header), "%s", header ? header : "");
snprintf(ps->footer, sizeof(ps->footer), "%s", footer ? footer : "");
}
void printBasSetContext(AppContextT *ctx) {
sBasCtx = ctx;
}
// True when the printer model in the settings can print pictures.
int32_t PrintCanPrintPictures(void) {
PrintSettingsT s;
printSettingsLoad(&s);
return basBool(printDriver(s.model) != NULL);
}
// The copies the user chose in the last PrintChoose.
int32_t PrintChoiceCopies(void) {
return sChoice.copies;
}
// Where the user chose to print in the last PrintChoose (a destination
// number).
int32_t PrintChoiceDest(void) {
return sChoice.dest;
}
// The file name chosen in the last PrintChoose when printing to a file.
const char *PrintChoiceFile(void) {
return platformPathNative(sChoice.filePath);
}
// The page range chosen in the last PrintChoose (0 and 0: every page).
int32_t PrintChoiceFirst(void) {
return sChoice.firstPage;
}
int32_t PrintChoiceLast(void) {
return sChoice.lastPage;
}
// The Print dialog alone (printer, copies and, when pageCount is above
// 0, a page range), for a program that prints its own output; read the
// answer with the PrintChoice functions. True when the user chose Print.
int32_t PrintChoose(const char *title, int32_t pageCount) {
if (!sBasCtx) {
return BAS_FALSE;
}
return basBool(printDialog(sBasCtx, title, pageCount, &sChoice));
}
int32_t PrintDestCount(void) {
return PrintDestCountE;
}
// True for the destination that prints into a file (it needs a name).
int32_t PrintDestIsFile(int32_t dest) {
return basBool(dest == PrintDestFileE);
}
// "" for a number out of range.
const char *PrintDestLabel(int32_t dest) {
return destValid(dest) ? printDestLabel((PrintDestE)dest) : "";
}
int32_t PrintModelCount(void) {
return PrintModelCountE;
}
// "" for a number out of range.
const char *PrintModelLabel(int32_t model) {
return modelValid(model) ? printModelLabel((PrintModelE)model) : "";
}
// Pages text takes with the page setup and printer in the settings.
int32_t PrintPageCount(const char *text) {
PrintSettingsT s;
printSettingsLoad(&s);
return printTextPageCount(text ? text : "", text ? (int32_t)strlen(text) : 0, &s.page, s.model);
}
// Lines per page of the page setup in prefs on printer model (the model's
// usual page length when the setup says 0); -1 for a model out of range.
int32_t PrintPageLines(int32_t prefs, int32_t model) {
PrintSettingsT s;
if (!modelValid(model)) {
return BAS_NONE;
}
printSettingsRead(prefsBasHandle(prefs), &s);
return printPageLines(&s.page, (PrintModelE)model);
}
// A number of the page setup in prefs: "Lines" (0 = the printer's usual
// page length), "Columns", "MarginTop", "MarginBottom", "MarginLeft",
// "MarginRight", "TabWidth", "Wrap" (True / False); -1 for an unknown
// field.
int32_t PrintPageSetting(int32_t prefs, const char *field) {
PrintSettingsT s;
if (!field) {
return BAS_NONE;
}
printSettingsRead(prefsBasHandle(prefs), &s);
if (strcasecmp(field, BAS_PAGE_WRAP) == 0) {
return basBool(s.page.wrap);
}
for (int32_t i = 0; i < BAS_PAGE_FIELD_COUNT; i++) {
if (strcasecmp(field, sPageFields[i].name) == 0) {
return *(const int32_t *)((const char *)&s.page + sPageFields[i].offset);
}
}
return BAS_NONE;
}
// Put a page setup into the [printer] settings of prefs (the printer
// stays as it is there); save prefs afterwards. False for a bad handle
// or a setup PrintPageSetupProblem$ finds a problem with.
int32_t PrintPageSettingsStore(int32_t prefs, int32_t lines, int32_t columns, int32_t marginTop, int32_t marginBottom, int32_t marginLeft, int32_t marginRight, int32_t tabWidth, int32_t wrap, const char *header, const char *footer) {
PrefsHandleT *h = prefsBasHandle(prefs);
PrintSettingsT s;
if (!h) {
return BAS_FALSE;
}
printSettingsRead(h, &s);
pageSetupFrom(&s.page, lines, columns, marginTop, marginBottom, marginLeft, marginRight, tabWidth, wrap, header, footer);
if (printPageSetupProblem(&s.page)) {
return BAS_FALSE;
}
printSettingsWrite(h, &s);
return BAS_TRUE;
}
int32_t PrintPageSetup(void) {
return basBool(sBasCtx && printPageSetupDialog(sBasCtx));
}
// "" when this page setup can be used, else why not.
const char *PrintPageSetupProblem(int32_t lines, int32_t columns, int32_t marginTop, int32_t marginBottom, int32_t marginLeft, int32_t marginRight, int32_t tabWidth, int32_t wrap, const char *header, const char *footer) {
PrintPageSetupT ps;
const char *problem;
pageSetupFrom(&ps, lines, columns, marginTop, marginBottom, marginLeft, marginRight, tabWidth, wrap, header, footer);
problem = printPageSetupProblem(&ps);
return problem ? problem : "";
}
// A text of the page setup in prefs: "Header" or "Footer"; "" for an
// unknown field.
const char *PrintPageText(int32_t prefs, const char *field) {
PrintSettingsT s;
sText[0] = '\0';
if (!field) {
return sText;
}
printSettingsRead(prefsBasHandle(prefs), &s);
if (strcasecmp(field, BAS_PAGE_HEADER) == 0) {
snprintf(sText, sizeof(sText), "%s", s.page.header);
} else if (strcasecmp(field, BAS_PAGE_FOOTER) == 0) {
snprintf(sText, sizeof(sText), "%s", s.page.footer);
}
return sText;
}
int32_t PrintPicture(const char *path, const char *title) {
return basBool(sBasCtx && printUiImageFile(sBasCtx, path, title, false));
}
int32_t PrintPictureDialog(const char *path, const char *title) {
return basBool(sBasCtx && printUiImageFile(sBasCtx, path, title, true));
}
// True when printer port LPTn (1-3) exists.
int32_t PrintPortPresent(int32_t port) {
return basBool(port >= BAS_LPT_FIRST && port < BAS_LPT_FIRST + PRINT_PORT_COUNT && printPortPresent(port - BAS_LPT_FIRST));
}
// The PRINT_STATUS_ bits of printer port LPTn (1-3); 0 for a port out of
// range.
int32_t PrintPortStatus(int32_t port) {
if (port < BAS_LPT_FIRST || port >= BAS_LPT_FIRST + PRINT_PORT_COUNT) {
return 0;
}
return printPortStatus(port - BAS_LPT_FIRST);
}
// The destination in the [printer] settings of a prefs.bas handle
// (the defaults for 0).
int32_t PrintSettingDest(int32_t prefs) {
PrintSettingsT s;
printSettingsRead(prefsBasHandle(prefs), &s);
return s.dest;
}
// The file printed into when the destination is a file.
const char *PrintSettingFile(int32_t prefs) {
static char path[PRINT_PATH_MAX];
PrintSettingsT s;
printSettingsRead(prefsBasHandle(prefs), &s);
snprintf(path, sizeof(path), "%s", s.filePath);
return path;
}
int32_t PrintSettingModel(int32_t prefs) {
PrintSettingsT s;
printSettingsRead(prefsBasHandle(prefs), &s);
return s.model;
}
// "" when the choice can be used, else why not.
const char *PrintSettingsProblem(int32_t dest, const char *filePath) {
const char *problem;
if (!destValid(dest)) {
return "Choose where the printer is connected.";
}
problem = printDeviceProblem((PrintDestE)dest, filePath);
return problem ? problem : "";
}
// Store model, destination and file into the [printer] settings of a
// prefs.bas handle (the page setup as it is in DVX.INI now). False for
// a bad handle or a number out of range.
int32_t PrintSettingsStore(int32_t prefs, int32_t model, int32_t dest, const char *filePath) {
PrefsHandleT *h = prefsBasHandle(prefs);
if (!h || !modelValid(model) || !destValid(dest)) {
return BAS_FALSE;
}
printDeviceStore(h, (PrintModelE)model, (PrintDestE)dest, filePath);
return BAS_TRUE;
}
// What is wrong with a printer whose status (PrintPortStatus) is status
// -- after writing a byte when afterWrite is True -- for printer where
// ("LPT1"); "" when nothing is.
const char *PrintStatusProblem(int32_t status, int32_t afterWrite, const char *where) {
sText[0] = '\0';
if (!printStatusProblem((uint8_t)status, afterWrite != 0, where ? where : "", sText, sizeof(sText))) {
sText[0] = '\0';
}
return sText;
}
int32_t PrintText(const char *text, const char *title) {
return basBool(sBasCtx && printUiText(sBasCtx, text, title, false));
}
int32_t PrintTextDialog(const char *text, const char *title) {
return basBool(sBasCtx && printUiText(sBasCtx, text, title, true));
}
// Print text without asking first on the printer in the settings: pages
// firstPage to lastPage (0 and 0: every page), copies times.
int32_t PrintTextPages(const char *text, const char *title, int32_t firstPage, int32_t lastPage, int32_t copies) {
PrintSettingsT s;
PrintChoiceT ch;
if (!sBasCtx) {
return BAS_FALSE;
}
printSettingsLoad(&s);
memset(&ch, 0, sizeof(ch));
ch.dest = s.dest;
ch.firstPage = firstPage;
ch.lastPage = lastPage;
ch.copies = copies;
snprintf(ch.filePath, sizeof(ch.filePath), "%s", s.filePath);
return basBool(printUiTextChoice(sBasCtx, text, title, &ch));
}