DVX_GUI/src/apps/kpunch/iconed/iconed.frm
2026-10-14 17:37:03 -05:00

661 lines
16 KiB
Text

VERSION DVX 1.00
Begin Form IconEd
Caption = "DVX Icon Editor"
Layout = VBox
AutoSize = True
Resizable = False
Centered = True
Begin Menu mnuFile
Caption = "&File"
Begin Menu mnuNew
Caption = "&New"
End
Begin Menu mnuOpen
Caption = "&Open..."
End
Begin Menu mnuSave
Caption = "&Save..."
End
Begin Menu mnuPrint
Caption = "&Print..."
End
Begin Menu mnuSep1
Caption = "-"
End
Begin Menu mnuExit
Caption = "E&xit"
End
End
Begin Menu mnuTools
Caption = "&Tools"
Begin Menu mnuPencil
Caption = "&Pencil"
Checked = True
RadioCheck = True
End
Begin Menu mnuEraser
Caption = "&Eraser"
RadioCheck = True
End
Begin Menu mnuPicker
Caption = "Color Pic&ker"
RadioCheck = True
End
End
Begin Menu mnuSize
Caption = "&Size"
Begin Menu mnuSize16
Caption = "16 x 16"
RadioCheck = True
End
Begin Menu mnuSize24
Caption = "24 x 24"
RadioCheck = True
End
Begin Menu mnuSize32
Caption = "32 x 32"
Checked = True
RadioCheck = True
End
End
Begin Menu mnuHelp
Caption = "&Help"
Begin Menu mnuHelpContents
Caption = "&Contents"
End
Begin Menu mnuHelpSep
Caption = "-"
End
Begin Menu mnuAbout
Caption = "&About Icon Editor..."
End
End
Begin Frame fraMain
Caption = ""
Layout = HBox
Weight = 100
Begin PictureBox cvEditor
Width = 256
Height = 256
Weight = 0
End
Begin Frame fraSidebar
Caption = ""
Layout = VBox
Width = 100
Weight = 0
Begin Label lblPreview
Caption = "Preview:"
End
Begin PictureBox cvPreview
Width = 32
Height = 32
Weight = 0
End
Begin Label lblColor
Caption = "Color:"
End
Begin PictureBox cvSelColor
Width = 32
Height = 20
Weight = 0
End
Begin Label lblPalette
Caption = "Palette:"
End
Begin PictureBox cvPalette
Width = 80
Height = 80
Weight = 0
End
End
End
Begin Label lblStatus
Caption = "Ready. (0, 0)"
Height = 16
Weight = 0
End
End
' 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.
OPTION EXPLICIT
' Pixels are held in pixels() at their exact RGB values. The preview
' canvas (used for Load and Save) stores the display's pixel format, so
' in 15/16-bit and 256-color display modes colors are rounded to what the
' display can show when an icon is opened or saved.
CONST CANVAS_SIZE = 256
CONST BG_COLOR = &H00C0C0C0
CONST GRID_COLOR = &H00808080
CONST EDGE_COLOR = &H00404040
CONST TOOL_PENCIL = 0
CONST TOOL_ERASER = 1
CONST TOOL_PICKER = 2
CONST PAL_COLS = 8
CONST PAL_ROWS = 4
CONST PAL_COUNT = 32 ' PAL_COLS * PAL_ROWS (CONST takes only literals)
CONST PAL_CELL = 10
CONST SIZE_SMALL = 16
CONST SIZE_MEDIUM = 24
CONST MAX_ICON = 32
CONST HELP_FILE = "iconed.hlp"
CONST OPEN_FILTER = "Icon Images (*.bmp;*.png)|All Files (*.*)"
CONST SAVE_FILTER = "PNG Images (*.png)|All Files (*.*)"
DIM SHARED pixels(MAX_ICON - 1, MAX_ICON - 1) AS LONG
DIM SHARED palette(PAL_COUNT - 1) AS LONG
DIM SHARED iconW AS INTEGER
DIM SHARED iconH AS INTEGER
DIM SHARED pixelSize AS INTEGER
DIM SHARED curTool AS INTEGER
DIM SHARED curColor AS LONG
DIM SHARED drawing AS BOOLEAN
DIM SHARED dirty AS BOOLEAN
DIM SHARED filePath AS STRING
SUB InitPalette
palette(0) = &H00000000
palette(1) = &H00FFFFFF
palette(2) = &H00C0C0C0
palette(3) = &H00808080
palette(4) = &H000000FF
palette(5) = &H00000080
palette(6) = &H000080FF
palette(7) = &H00004080
palette(8) = &H0000FFFF
palette(9) = &H00008080
palette(10) = &H0000FF00
palette(11) = &H00008000
palette(12) = &H00FFFF00
palette(13) = &H00808000
palette(14) = &H0080FF00
palette(15) = &H00404000
palette(16) = &H00FF0000
palette(17) = &H00800000
palette(18) = &H00FF0080
palette(19) = &H00800080
palette(20) = &H00FF00FF
palette(21) = &H008000FF
palette(22) = &H00C080FF
palette(23) = &H00FF4040
palette(24) = &H00404040
palette(25) = &H00A0A0A0
palette(26) = &H00E0E0E0
palette(27) = &H00C0E0FF
palette(28) = &H00E0FFC0
palette(29) = &H00FFE0C0
palette(30) = &H00C0C0FF
palette(31) = &H00FFC0C0
END SUB
SUB ClearPixels
DIM x AS INTEGER
DIM y AS INTEGER
FOR y = 0 TO MAX_ICON - 1
FOR x = 0 TO MAX_ICON - 1
pixels(x, y) = BG_COLOR
NEXT x
NEXT y
dirty = False
END SUB
' The preview canvas always matches the icon size, so Save writes exactly
' iconW x iconH pixels.
SUB SetIconSize(w AS INTEGER)
iconW = w
iconH = w
pixelSize = CANVAS_SIZE \ w
cvPreview.Resize w, w
END SUB
' Checks the Size menu item for the current icon size.
SUB CheckSizeMenu
IF iconW = SIZE_SMALL THEN
mnuSize16.Checked = True
ELSEIF iconW = SIZE_MEDIUM THEN
mnuSize24.Checked = True
ELSE
mnuSize32.Checked = True
END IF
END SUB
FUNCTION IsIconSize(w AS INTEGER) AS BOOLEAN
IsIconSize = (w = SIZE_SMALL OR w = SIZE_MEDIUM OR w = MAX_ICON)
END FUNCTION
SUB DrawEditorGrid
DIM x AS INTEGER
DIM y AS INTEGER
DIM edgeW AS INTEGER
cvEditor.Clear BG_COLOR
FOR y = 0 TO iconH - 1
FOR x = 0 TO iconW - 1
cvEditor.FillRect x * pixelSize, y * pixelSize, pixelSize, pixelSize, pixels(x, y)
NEXT x
NEXT y
FOR x = 0 TO iconW
cvEditor.DrawLine x * pixelSize, 0, x * pixelSize, iconH * pixelSize, GRID_COLOR
NEXT x
FOR y = 0 TO iconH
cvEditor.DrawLine 0, y * pixelSize, iconW * pixelSize, y * pixelSize, GRID_COLOR
NEXT y
edgeW = iconW * pixelSize
cvEditor.DrawRect 0, 0, edgeW, edgeW, EDGE_COLOR
cvEditor.Refresh
END SUB
SUB DrawPreview
DIM x AS INTEGER
DIM y AS INTEGER
FOR y = 0 TO iconH - 1
FOR x = 0 TO iconW - 1
cvPreview.SetPixel x, y, pixels(x, y)
NEXT x
NEXT y
cvPreview.Refresh
END SUB
SUB DrawPalette
DIM i AS INTEGER
DIM px AS INTEGER
DIM py AS INTEGER
cvPalette.Clear BG_COLOR
FOR i = 0 TO PAL_COUNT - 1
px = (i MOD PAL_COLS) * PAL_CELL
py = (i \ PAL_COLS) * PAL_CELL
cvPalette.FillRect px, py, PAL_CELL, PAL_CELL, palette(i)
cvPalette.DrawRect px, py, PAL_CELL, PAL_CELL, &H00000000
NEXT i
cvPalette.Refresh
END SUB
SUB DrawSelectedColor
cvSelColor.Clear curColor
cvSelColor.Refresh
END SUB
SUB RefreshAll
DrawEditorGrid
DrawPreview
DrawSelectedColor
END SUB
SUB UpdateTitle
DIM t AS STRING
t = "DVX Icon Editor - " + LTRIM$(STR$(iconW)) + "x" + LTRIM$(STR$(iconH))
IF filePath <> "" THEN
t = t + " - " + filePath
END IF
IF dirty THEN
t = t + " *"
END IF
IconEd.Caption = t
END SUB
SUB SetIconPixel(x AS INTEGER, y AS INTEGER)
DIM c AS LONG
IF x < 0 OR x >= iconW OR y < 0 OR y >= iconH THEN
EXIT SUB
END IF
IF curTool = TOOL_ERASER THEN
c = BG_COLOR
ELSE
c = curColor
END IF
IF pixels(x, y) <> c THEN
pixels(x, y) = c
' The caption only changes on the clean-to-dirty transition.
IF NOT dirty THEN
dirty = True
UpdateTitle
END IF
cvEditor.FillRect x * pixelSize, y * pixelSize, pixelSize, pixelSize, c
cvEditor.DrawLine x * pixelSize, y * pixelSize, (x + 1) * pixelSize, y * pixelSize, GRID_COLOR
cvEditor.DrawLine x * pixelSize, y * pixelSize, x * pixelSize, (y + 1) * pixelSize, GRID_COLOR
cvEditor.DrawLine (x + 1) * pixelSize, y * pixelSize, (x + 1) * pixelSize, (y + 1) * pixelSize, GRID_COLOR
cvEditor.DrawLine x * pixelSize, (y + 1) * pixelSize, (x + 1) * pixelSize, (y + 1) * pixelSize, GRID_COLOR
cvPreview.SetPixel x, y, c
END IF
END SUB
SUB SelectTool(t AS INTEGER)
curTool = t
IF t = TOOL_PENCIL THEN
mnuPencil.Checked = True
lblStatus.Caption = "Tool: Pencil"
ELSEIF t = TOOL_ERASER THEN
mnuEraser.Checked = True
lblStatus.Caption = "Tool: Eraser"
ELSEIF t = TOOL_PICKER THEN
mnuPicker.Checked = True
lblStatus.Caption = "Tool: Picker"
END IF
END SUB
SUB ChangeSize(newSize AS INTEGER)
DIM ans AS INTEGER
IF newSize = iconW THEN
EXIT SUB
END IF
IF dirty THEN
ans = MsgBox("Unsaved changes will be lost. Change size?", vbYesNo)
IF ans = vbNo THEN
' The menu already moved the radio check; put it back.
CheckSizeMenu
EXIT SUB
END IF
END IF
SetIconSize newSize
ClearPixels
filePath = ""
CheckSizeMenu
UpdateTitle
RefreshAll
lblStatus.Caption = STR$(iconW) + "x" + STR$(iconH) + " - Ready."
END SUB
' Writes the preview canvas to path. Returns False when the file could
' not be written.
FUNCTION SaveIcon(path AS STRING) AS BOOLEAN
DrawPreview
SaveIcon = (cvPreview.Save(path) = 0)
END FUNCTION
SUB IconEd_Load
InitPalette
SetIconSize MAX_ICON
ClearPixels
curTool = TOOL_PENCIL
curColor = &H00000000
drawing = False
filePath = ""
DrawPalette
RefreshAll
UpdateTitle
' An icon named on the command line (a document opened from Program
' Manager or File Manager) is loaded at once.
IF COMMAND$(1) <> "" THEN
OpenIcon COMMAND$(1)
END IF
END SUB
SUB mnuNew_Click
DIM ans AS INTEGER
IF dirty THEN
ans = MsgBox("Unsaved changes. Start new icon?", vbYesNo)
IF ans = vbNo THEN
EXIT SUB
END IF
END IF
ClearPixels
filePath = ""
UpdateTitle
RefreshAll
END SUB
SUB mnuOpen_Click
DIM ans AS INTEGER
DIM path AS STRING
IF dirty THEN
ans = MsgBox("Unsaved changes. Open another file?", vbYesNo)
IF ans = vbNo THEN
EXIT SUB
END IF
END IF
path = DlgFileOpen$("Open Icon", OPEN_FILTER)
IF path = "" THEN
EXIT SUB
END IF
OpenIcon path
END SUB
' Loads the icon in path; a file that is not a 16, 24 or 32 pixel square
' image is refused (reported) and the current icon stays.
SUB OpenIcon(path AS STRING)
DIM w AS INTEGER
DIM x AS INTEGER
DIM y AS INTEGER
' A failed Load leaves the preview unchanged; a successful one gives
' it the image's size.
IF cvPreview.Load(path) <> 0 THEN
MsgBox "Could not load " + path + "."
EXIT SUB
END IF
w = cvPreview.ImageWidth
IF cvPreview.ImageHeight <> w OR NOT IsIconSize(w) THEN
' Put the current icon back into the preview.
cvPreview.Resize iconW, iconH
DrawPreview
MsgBox "Icons must be 16 x 16, 24 x 24, or 32 x 32 pixels."
EXIT SUB
END IF
SetIconSize w
ClearPixels
FOR y = 0 TO iconH - 1
FOR x = 0 TO iconW - 1
pixels(x, y) = cvPreview.GetPixel(x, y)
NEXT x
NEXT y
filePath = path
CheckSizeMenu
UpdateTitle
RefreshAll
END SUB
SUB mnuSave_Click
DIM path AS STRING
path = DlgFileSave$("Save Icon", SAVE_FILTER)
IF path = "" THEN
EXIT SUB
END IF
IF NOT SaveIcon(path) THEN
MsgBox "Could not save " + path + "."
EXIT SUB
END IF
filePath = path
dirty = False
UpdateTitle
lblStatus.Caption = "Saved: " + path
END SUB
' The printing library prints picture files, so the icon goes through a
' scratch copy in the TEMP folder (else the program's user folder).
SUB mnuPrint_Click
DIM tmp AS STRING
DIM title AS STRING
tmp = FileOpsTempDir$()
IF tmp = "" THEN
tmp = App.User
END IF
tmp = FileOpsPathJoin$(tmp, "PRINTICO.BMP")
IF NOT SaveIcon(tmp) THEN
MsgBox "Could not prepare the icon for printing."
EXIT SUB
END IF
IF filePath <> "" THEN
title = filePath
ELSE
title = "Untitled icon"
END IF
PrintPictureDialog tmp, title
KILL tmp
END SUB
SUB mnuExit_Click
Unload IconEd
END SUB
SUB mnuPencil_Click
SelectTool TOOL_PENCIL
END SUB
SUB mnuEraser_Click
SelectTool TOOL_ERASER
END SUB
SUB mnuPicker_Click
SelectTool TOOL_PICKER
END SUB
SUB mnuSize16_Click
ChangeSize SIZE_SMALL
END SUB
SUB mnuSize24_Click
ChangeSize SIZE_MEDIUM
END SUB
SUB mnuSize32_Click
ChangeSize MAX_ICON
END SUB
SUB cvEditor_MouseDown(button AS INTEGER, x AS INTEGER, y AS INTEGER)
DIM px AS INTEGER
DIM py AS INTEGER
px = x \ pixelSize
py = y \ pixelSize
IF curTool = TOOL_PICKER THEN
IF px >= 0 AND px < iconW AND py >= 0 AND py < iconH THEN
curColor = pixels(px, py)
DrawSelectedColor
SelectTool TOOL_PENCIL
END IF
EXIT SUB
END IF
drawing = True
SetIconPixel px, py
lblStatus.Caption = "(" + STR$(px) + "," + STR$(py) + ")"
END SUB
SUB cvEditor_MouseMove(button AS INTEGER, x AS INTEGER, y AS INTEGER)
DIM px AS INTEGER
DIM py AS INTEGER
px = x \ pixelSize
py = y \ pixelSize
IF px >= 0 AND px < iconW AND py >= 0 AND py < iconH THEN
lblStatus.Caption = "(" + STR$(px) + "," + STR$(py) + ")"
END IF
IF drawing THEN
SetIconPixel px, py
END IF
END SUB
SUB cvEditor_MouseUp(button AS INTEGER, x AS INTEGER, y AS INTEGER)
drawing = False
END SUB
SUB IconEd_QueryUnload(Cancel AS INTEGER)
IF NOT dirty THEN
EXIT SUB
END IF
DIM result AS INTEGER
result = DlgPromptSave("Icon Editor")
IF result = DVX_SAVE_YES THEN
mnuSave_Click
IF dirty THEN
Cancel = 1
END IF
ELSEIF result = DVX_SAVE_CANCEL THEN
Cancel = 1
END IF
END SUB
SUB cvPalette_MouseDown(button AS INTEGER, x AS INTEGER, y AS INTEGER)
DIM col AS INTEGER
DIM row AS INTEGER
DIM idx AS INTEGER
col = x \ PAL_CELL
row = y \ PAL_CELL
IF col >= 0 AND col < PAL_COLS AND row >= 0 AND row < PAL_ROWS THEN
idx = row * PAL_COLS + col
curColor = palette(idx)
DrawSelectedColor
IF curTool = TOOL_PICKER THEN
SelectTool TOOL_PENCIL
END IF
END IF
END SUB
SUB mnuHelpContents_Click
HelpView HELP_FILE
END SUB
SUB mnuAbout_Click
DIM msg AS STRING
msg = "Icon Editor" + CHR$(10) + CHR$(10)
msg = msg + "Draws small square pictures and icons." + CHR$(10) + CHR$(10)
msg = msg + "Copyright 2026 Scott Duensing"
MsgBox msg, vbOKOnly + vbInformation, "About Icon Editor"
END SUB