1669 lines
64 KiB
C#
1669 lines
64 KiB
C#
using MessagePack;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Drawing;
|
|
using System.Drawing.Drawing2D;
|
|
using System.Drawing.Imaging;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using System.Windows.Forms;
|
|
|
|
|
|
namespace McBitFont {
|
|
public partial class MainForm : Form {
|
|
|
|
|
|
[Serializable]
|
|
[MessagePackObject]
|
|
public struct FrameMiniature {
|
|
public FrameMiniature(int cc, int ww, int hh) {
|
|
code = cc;
|
|
width = ww;
|
|
height = hh;
|
|
data = new bool[ww, hh];
|
|
}
|
|
[Key(0)]
|
|
public int code;
|
|
[Key(1)]
|
|
public int width;
|
|
[Key(2)]
|
|
public int height;
|
|
[Key(3)]
|
|
public bool[,] data;
|
|
};
|
|
|
|
[Serializable]
|
|
[MessagePackObject]
|
|
public struct SaveBlock {
|
|
[Key(4)]
|
|
public bool monospaced;
|
|
[Key(5)]
|
|
public int codepage;
|
|
[Key(6)]
|
|
public int baseline;
|
|
[Key(7)]
|
|
public List<FrameMiniature> frames;
|
|
}
|
|
|
|
public FrameMiniature f;
|
|
public List<FrameMiniature> frames = new List<FrameMiniature>();
|
|
//private CanvasHistory history = new();
|
|
private ChangeHistory history;
|
|
private int cellSize = 10;
|
|
public int dotWidth, dotHeight;
|
|
private readonly int pixelOffset = 5;
|
|
private int gap;
|
|
private int w, h;
|
|
public bool monospaced = false;
|
|
private bool modified = false;
|
|
private bool prjModified = false;
|
|
public const string version = "2.5";
|
|
public string prjName = "Untitled";
|
|
public string prjFileName = "";
|
|
public int codepage = 1251;
|
|
private FrameMiniature fbuf;
|
|
private readonly DataFormats.Format clpbFormat = DataFormats.GetFormat("McBitFontFrame");
|
|
private int baseline = 0;
|
|
private bool set_base = false;
|
|
private Point selection1, selection2;
|
|
private Point[,] sidebarLocs = new Point[2, 3];
|
|
|
|
|
|
public MainForm() {
|
|
InitializeComponent();
|
|
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
|
this.dotPanel.MouseWheel += new MouseEventHandler(this.DotPanel_MouseWheel);
|
|
}
|
|
|
|
public void SetNewWH() {
|
|
w = pixelOffset + dotWidth * (cellSize + gap);
|
|
h = pixelOffset + dotHeight * (cellSize + gap);
|
|
}
|
|
|
|
private void UpdateSelectionLabel(int width, int height) {
|
|
lblSelection.Text = width.ToString() + ',' + height.ToString();
|
|
}
|
|
|
|
public void SetModified(bool modif = true, bool prj = false) {
|
|
string suffix = "";
|
|
if (prj) {
|
|
prjModified = modif;
|
|
if (modif) suffix = " *";
|
|
SetWindowCap(suffix);
|
|
} else {
|
|
modified = modif;
|
|
lblModified.Visible = modif;
|
|
}
|
|
}
|
|
|
|
// Check if a frame is "blank"
|
|
private static bool IsFrameBlank(FrameMiniature frame) {
|
|
if (frame.code == 32) return false; // Space character is always blank, so skip it
|
|
for (int i = 0; i < frame.width; i++) {
|
|
for (int j = 0; j < frame.height; j++) {
|
|
if (frame.data[i, j]) return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Remember sidebar panels locations
|
|
private void SideBarRecalc() {
|
|
|
|
sidebarLocs[0, 0] = new Point(this.Width - 459, 31);
|
|
sidebarLocs[0, 1] = new Point(this.Width - 316, 24);
|
|
sidebarLocs[0, 2] = new Point(this.Width - 487, 31);
|
|
sidebarLocs[1, 0] = new Point(this.Width - panel1.Width - 70, 180);
|
|
sidebarLocs[1, 1] = new Point(this.Width - pnlInfo.Width - 110, 320);
|
|
sidebarLocs[1, 2] = new Point(dotPanel.Width + 17, 31);
|
|
}
|
|
|
|
private void Form1_Load(object sender, EventArgs e) {
|
|
lblType.Text = monospaced ? "Monospaced" : "Variable width / Single";
|
|
tsmiMakeVarWidth.Visible = monospaced;
|
|
makeVarWidthToolStripMenuItem.Visible = monospaced;
|
|
dotWidth = (int)nudX.Value;
|
|
dotHeight = (int)nudY.Value;
|
|
|
|
selection1 = new Point(0, 0);
|
|
selection2 = new Point(dotWidth - 1, dotHeight - 1);
|
|
UpdateSelectionLabel(dotWidth, dotHeight);
|
|
|
|
gap = (cellSize < 5) ? 0 : 1;
|
|
SetNewWH();
|
|
|
|
|
|
cbZoom.SelectedIndex = 3;
|
|
cbZoom.SelectedIndexChanged += cbZoom_SelectedIndexChanged;
|
|
|
|
frames.Add(new FrameMiniature(0, dotWidth, dotHeight));
|
|
miniList.Items.Add("000", "000 Single", "000");
|
|
miniList.Refresh();
|
|
miniList.Items[0].Selected = true;
|
|
miniList.Select();
|
|
f = CopyFrame(frames.Find(x => x.code == 0));
|
|
|
|
ListViewItem_SetSpacing(miniList, 50 + 1, 50 + 22);
|
|
|
|
SetWindowCap();
|
|
|
|
fbuf = new FrameMiniature(0, dotWidth, dotHeight);
|
|
|
|
history = new(this);
|
|
|
|
// Chek for arguments
|
|
if (Environment.GetCommandLineArgs().Length > 1) {
|
|
LoadProject(Environment.GetCommandLineArgs()[1]);
|
|
}
|
|
lblType.Text = monospaced ? "Monospaced" : "Variable width / Single";
|
|
tsmiMakeVarWidth.Visible = monospaced;
|
|
makeVarWidthToolStripMenuItem.Visible = monospaced;
|
|
tsmiCodeShift.Visible = frames.Count > 1;
|
|
CodeShiftToolStripMenuItem.Visible = frames.Count > 1;
|
|
|
|
CheckForAdd();
|
|
|
|
SideBarRecalc();
|
|
}
|
|
|
|
[DllImport("user32.dll")]
|
|
public static extern int SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
|
|
|
|
public static int MakeLong(short lowPart, short highPart) {
|
|
return (int)(((ushort)lowPart) | (uint)(highPart << 16));
|
|
}
|
|
|
|
public FrameMiniature CopyFrame(FrameMiniature frame, bool clipboard = false) {
|
|
int width = chkRectSelect.Checked && clipboard ? selection2.X - selection1.X + 1 : frame.width;
|
|
int height = chkRectSelect.Checked && clipboard ? selection2.Y - selection1.Y + 1 : frame.height;
|
|
var ff = new FrameMiniature(frame.code, width, height);
|
|
|
|
if (chkRectSelect.Checked && clipboard) {
|
|
for (int i = 0; i <= selection2.X - selection1.X; i++)
|
|
for (int j = 0; j <= selection2.Y - selection1.Y; j++) {
|
|
ff.data[i, j] = frame.data[i + selection1.X, j + selection1.Y];
|
|
}
|
|
} else
|
|
Array.Copy(frame.data, ff.data, frame.data.Length);
|
|
|
|
return ff;
|
|
}
|
|
|
|
public static void ListViewItem_SetSpacing(ListView listview, short leftPadding, short topPadding) {
|
|
const int LVM_FIRST = 0x1000;
|
|
const int LVM_SETICONSPACING = LVM_FIRST + 53;
|
|
SendMessage(listview.Handle, LVM_SETICONSPACING, IntPtr.Zero, (IntPtr)MakeLong(leftPadding, topPadding));
|
|
}
|
|
|
|
private void DotPanel_MouseWheel(object sender, MouseEventArgs e) {
|
|
int t = e.Delta / 120;
|
|
if (e.Delta == 0) return;
|
|
if (ModifierKeys.HasFlag(Keys.Control)) {
|
|
t += cbZoom.SelectedIndex;
|
|
if (t > cbZoom.Items.Count - 1) return;
|
|
if (t < 0) return;
|
|
cbZoom.SelectedIndex = t;
|
|
} else if (ModifierKeys.HasFlag(Keys.Shift)) {
|
|
if (hScroll.Enabled) {
|
|
t = t * -1 * (cellSize + gap) + hScroll.Value;
|
|
if (t < hScroll.Minimum) t = hScroll.Minimum;
|
|
if (t > hScroll.Maximum) t = hScroll.Maximum;
|
|
hScroll.Value = t;
|
|
}
|
|
} else {
|
|
if (vScroll.Enabled) {
|
|
t = t * -1 * (cellSize + gap) + vScroll.Value;
|
|
if (t < vScroll.Minimum) t = vScroll.Minimum;
|
|
if (t > vScroll.Maximum) t = vScroll.Maximum;
|
|
vScroll.Value = t;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void nudX_ValueChanged(object sender, EventArgs e) {
|
|
Cursor.Current = Cursors.WaitCursor;
|
|
if (monospaced) {
|
|
Bitmap bmp;
|
|
for (int i = 0; i < frames.Count; i++) {
|
|
frames[i] = FrameResize(frames[i], (int)nudX.Value, dotHeight);
|
|
bmp = GetMiniPictue(frames[i]);
|
|
string s = frames[i].code.ToString().PadLeft(3, '0');
|
|
ilMiniatures.Images.RemoveByKey(s);
|
|
ilMiniatures.Images.Add(s, (Image)bmp);
|
|
miniList.Items[s].ImageKey = s;
|
|
}
|
|
SetModified(true, true);
|
|
}
|
|
if (nudX.Focused) {
|
|
SetModified();
|
|
}
|
|
|
|
DotResize((int)nudX.Value, dotHeight);
|
|
if (monospaced) history.Add(frames);
|
|
else history.Add(f);
|
|
|
|
Cursor.Current = Cursors.Default;
|
|
}
|
|
|
|
public void nudY_ValueChanged(object sender, EventArgs e) {
|
|
Cursor.Current = Cursors.WaitCursor;
|
|
Bitmap bmp;
|
|
for (int i = 0; i < frames.Count; i++) {
|
|
frames[i] = FrameResize(frames[i], frames[i].width, (int)nudY.Value);
|
|
bmp = GetMiniPictue(frames[i]);
|
|
string s = frames[i].code.ToString().PadLeft(3, '0');
|
|
ilMiniatures.Images.RemoveByKey(s);
|
|
ilMiniatures.Images.Add(s, (Image)bmp);
|
|
miniList.Items[s].ImageKey = s;
|
|
}
|
|
if (nudY.Focused) {
|
|
SetModified();
|
|
SetModified(true, true);
|
|
}
|
|
|
|
DotResize(dotWidth, (int)nudY.Value);
|
|
history.Add(frames);
|
|
Cursor.Current = Cursors.Default;
|
|
}
|
|
|
|
private FrameMiniature FrameResize(FrameMiniature ff, int neww, int newh) {
|
|
int oldw = ff.width;
|
|
int oldh = ff.height;
|
|
int di = 0, dj = 0;
|
|
int imax, jmax;
|
|
bool[,] t;
|
|
|
|
if (chkLeftSide.Checked) di = oldw - neww;
|
|
if (chkTopSide.Checked) dj = oldh - newh;
|
|
|
|
imax = (neww < oldw) ? neww : oldw;
|
|
jmax = (newh < oldh) ? newh : oldh;
|
|
if (neww > oldw) imax -= di;
|
|
if (newh > oldh) jmax -= dj;
|
|
|
|
ff.width = neww;
|
|
ff.height = newh;
|
|
t = new bool[neww, newh];
|
|
for (int i = 0; i < imax; i++) {
|
|
for (int j = 0; j < jmax; j++) {
|
|
if (i + di >= 0 && j + dj >= 0) t[i, j] = ff.data[i + di, j + dj];
|
|
}
|
|
}
|
|
ff.data = t;
|
|
|
|
return ff;
|
|
}
|
|
|
|
private void DotResize(int ww, int hh) {
|
|
f = FrameResize(f, ww, hh);
|
|
dotWidth = ww;
|
|
dotHeight = hh;
|
|
if (selection2.X > dotWidth - 1) {
|
|
selection2.X = dotWidth - 1;
|
|
UpdateSelectionLabel(selection2.X - selection1.X + 1, selection2.Y - selection1.Y + 1);
|
|
}
|
|
if (selection2.Y > dotHeight - 1) {
|
|
selection2.Y = dotHeight - 1;
|
|
UpdateSelectionLabel(selection2.X - selection1.X + 1, selection2.Y - selection1.Y + 1);
|
|
}
|
|
SetNewWH();
|
|
cbZoom_SelectedIndexChanged(cbZoom, null);
|
|
}
|
|
|
|
private void cbZoom_SelectedIndexChanged(object sender, EventArgs e) {
|
|
cellSize = Convert.ToInt32(cbZoom.Text);
|
|
gap = (cellSize < 5) ? 0 : 1;
|
|
|
|
SetNewWH();
|
|
if (w <= dotPanel.Width) {
|
|
hScroll.Enabled = false;
|
|
hScroll.Value = 0;
|
|
vScroll.Maximum = 0;
|
|
} else {
|
|
hScroll.Maximum = w - dotPanel.Width + 12;
|
|
hScroll.Minimum = 0;
|
|
hScroll.Enabled = true;
|
|
}
|
|
|
|
if (h <= dotPanel.Height) {
|
|
vScroll.Enabled = false;
|
|
vScroll.Value = 0;
|
|
vScroll.Maximum = 0;
|
|
} else {
|
|
vScroll.Maximum = h - dotPanel.Height + 12;
|
|
vScroll.Minimum = 0;
|
|
vScroll.Enabled = true;
|
|
}
|
|
|
|
dotPanel.Refresh();
|
|
}
|
|
|
|
// Deconstruct selection coords from points
|
|
private (int x, int y, int x2, int y2) RectSelCoords() {
|
|
int x, y, x2, y2;
|
|
if (chkRectSelect.Checked) {
|
|
x = selection1.X; y = selection1.Y;
|
|
x2 = selection2.X; y2 = selection2.Y;
|
|
} else {
|
|
x = y = 0;
|
|
x2 = dotWidth - 1; y2 = dotHeight - 1;
|
|
}
|
|
return (x, y, x2, y2);
|
|
}
|
|
|
|
private void btnShiftUp_Click(object sender, EventArgs e) {
|
|
int x, y, x2, y2;
|
|
bool c;
|
|
|
|
(x, y, x2, y2) = RectSelCoords();
|
|
|
|
for (int i = x; i <= x2; i++) {
|
|
c = f.data[i, y];
|
|
for (int j = y; j <= y2; j++) {
|
|
if (j == y2) {
|
|
f.data[i, j] = c;
|
|
} else {
|
|
f.data[i, j] = f.data[i, j + 1];
|
|
}
|
|
}
|
|
}
|
|
history.Add(f);
|
|
CheckHistoryButtons();
|
|
SetModified();
|
|
dotPanel.Refresh();
|
|
}
|
|
|
|
private void btnShiftDown_Click(object sender, EventArgs e) {
|
|
int x, y, x2, y2;
|
|
bool c;
|
|
|
|
(x, y, x2, y2) = RectSelCoords();
|
|
|
|
for (int i = x; i <= x2; i++) {
|
|
c = f.data[i, y2];
|
|
for (int j = y2; j >= y; j--) {
|
|
if (j == y) {
|
|
f.data[i, j] = c;
|
|
} else {
|
|
f.data[i, j] = f.data[i, j - 1];
|
|
}
|
|
}
|
|
}
|
|
history.Add(f);
|
|
CheckHistoryButtons();
|
|
SetModified();
|
|
dotPanel.Refresh();
|
|
}
|
|
|
|
private void btnShiftLeft_Click(object sender, EventArgs e) {
|
|
int x, y, x2, y2;
|
|
bool c;
|
|
|
|
(x, y, x2, y2) = RectSelCoords();
|
|
|
|
for (int j = y; j <= y2; j++) {
|
|
c = f.data[x, j];
|
|
for (int i = x; i <= x2; i++) {
|
|
if (i == x2) {
|
|
f.data[i, j] = c;
|
|
} else {
|
|
f.data[i, j] = f.data[i + 1, j];
|
|
}
|
|
}
|
|
}
|
|
history.Add(f);
|
|
CheckHistoryButtons();
|
|
SetModified();
|
|
dotPanel.Refresh();
|
|
}
|
|
|
|
private void btnShiftRight_Click(object sender, EventArgs e) {
|
|
int x, y, x2, y2;
|
|
bool c;
|
|
(x, y, x2, y2) = RectSelCoords();
|
|
|
|
for (int j = y; j <= y2; j++) {
|
|
c = f.data[x2, j];
|
|
for (int i = x2; i >= x; i--) {
|
|
if (i == x) {
|
|
f.data[i, j] = c;
|
|
} else {
|
|
f.data[i, j] = f.data[i - 1, j];
|
|
}
|
|
}
|
|
}
|
|
history.Add(f);
|
|
CheckHistoryButtons();
|
|
SetModified();
|
|
dotPanel.Refresh();
|
|
}
|
|
|
|
private bool mouseDown = false; // Used in canvas history tracking and rectangle selection logics
|
|
private bool fChanged = false; // Used in canvas history (undo / redo) tracking
|
|
private bool mouseDownMiddle = false; // Used in middle mouse dragging logic
|
|
private int mouseX, mouseY; // To remember last mouse X and Y (used in middle mouse dragging logic)
|
|
private int lastX = 0, lastY = 0; // Used for drawing straight lines
|
|
private void dotPanel_MouseMove(object sender, MouseEventArgs e) {
|
|
var rectSel = chkRectSelect.Checked;
|
|
bool rectSelUpdated = false;
|
|
|
|
// Drag with middle mouse button
|
|
if (vScroll.Enabled || hScroll.Enabled) {
|
|
if (mouseDownMiddle) {
|
|
var dY = mouseY - e.Y <= -cellSize - gap || mouseY - e.Y >= cellSize + gap;
|
|
var dX = mouseX - e.X <= -cellSize - gap || mouseX - e.X >= cellSize + gap;
|
|
int newY = vScroll.Value;
|
|
int newX = hScroll.Value;
|
|
if (dX) {
|
|
newX += (mouseX - e.X);
|
|
if (newX < hScroll.Minimum) newX = hScroll.Minimum;
|
|
if (newX > hScroll.Maximum) newX = hScroll.Maximum;
|
|
mouseX = e.X;
|
|
hScroll.Value = newX;
|
|
}
|
|
if (dY) {
|
|
newY += (mouseY - e.Y);
|
|
if (newY < vScroll.Minimum) newY = vScroll.Minimum;
|
|
if (newY > vScroll.Maximum) newY = vScroll.Maximum;
|
|
mouseY = e.Y;
|
|
vScroll.Value = newY;
|
|
}
|
|
}
|
|
if (!mouseDownMiddle && e.Button == MouseButtons.Middle) {
|
|
mouseDownMiddle = true;
|
|
mouseX = e.X;
|
|
mouseY = e.Y;
|
|
}
|
|
if (mouseDownMiddle && e.Button == MouseButtons.None) {
|
|
mouseDownMiddle = false;
|
|
}
|
|
}
|
|
|
|
|
|
// Moving baseline
|
|
Rectangle rect1, rect2;
|
|
if (set_base) {
|
|
|
|
int yy = pixelOffset + baseline * (cellSize + gap) - vScroll.Value - 1;
|
|
rect1 = new Rectangle(pixelOffset, yy, w, 2);
|
|
baseline = (e.Y - pixelOffset + vScroll.Value) / (cellSize + gap);
|
|
|
|
if (baseline >= dotHeight) baseline = dotHeight - 1;
|
|
if (baseline < 0) baseline = 0;
|
|
|
|
yy = pixelOffset + baseline * (cellSize + gap) - vScroll.Value - 1;
|
|
rect2 = new Rectangle(pixelOffset, yy, w, 2);
|
|
|
|
dotPanel.Invalidate(rect1);
|
|
dotPanel.Invalidate(rect2);
|
|
|
|
if (e.Button == MouseButtons.Left) {
|
|
set_base = false;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (e.X >= w || e.X <= pixelOffset || e.Y >= h || e.Y <= pixelOffset) return;
|
|
// Change coordinates in the panel
|
|
int i = (e.X - pixelOffset + hScroll.Value) / (cellSize + gap);
|
|
int j = (e.Y - pixelOffset + vScroll.Value) / (cellSize + gap);
|
|
lblCoords.Text = i.ToString() + ',' + j.ToString();
|
|
|
|
// History and Rectangle selection management
|
|
if (rectSel && mouseDown && e.Button != MouseButtons.None) {
|
|
if (selection2.X != i || selection2.Y != j) {
|
|
rectSelUpdated = true;
|
|
selection2.X = i;
|
|
selection2.Y = j;
|
|
}
|
|
}
|
|
if (e.Button != MouseButtons.None && !mouseDown) {
|
|
// Started to move a mouse with button held
|
|
mouseDown = true;
|
|
if (rectSel) {
|
|
selection1.X = i;
|
|
selection1.Y = j;
|
|
selection2.X = i;
|
|
selection2.Y = j;
|
|
dotPanel.Invalidate();
|
|
} //else history.AddPre(f, false);
|
|
}
|
|
if (e.Button == MouseButtons.None && mouseDown) {
|
|
// Released a mouse button
|
|
mouseDown = false;
|
|
if (rectSel) {
|
|
NormPoints(ref selection1, ref selection2);
|
|
dotPanel.Invalidate();
|
|
} else {
|
|
if (fChanged) {
|
|
fChanged = false;
|
|
history.Add(f);
|
|
}
|
|
CheckHistoryButtons();
|
|
}
|
|
}
|
|
|
|
if (rectSel) {
|
|
if (mouseDown && rectSelUpdated) {
|
|
int x, xx, x2, xx2, y, yy, y2, yy2;
|
|
|
|
Point p1 = new(selection1.X, selection1.Y);
|
|
Point p2 = new(selection2.X, selection2.Y);
|
|
NormPoints(ref p1, ref p2);
|
|
|
|
UpdateSelectionLabel(p2.X - p1.X + 1, p2.Y - p1.Y + 1);
|
|
|
|
x = pixelOffset + (p1.X - 1) * (cellSize + gap) - hScroll.Value - 1;
|
|
y = pixelOffset + (p1.Y - 1) * (cellSize + gap) - vScroll.Value - 1;
|
|
x2 = pixelOffset + (p2.X + 2) * (cellSize + gap) - hScroll.Value - 1;
|
|
y2 = pixelOffset + (p2.Y + 2) * (cellSize + gap) - vScroll.Value - 1;
|
|
xx = x + 2 * (cellSize + gap);
|
|
yy = y + 2 * (cellSize + gap);
|
|
xx2 = x2 - 2 * (cellSize + gap);
|
|
yy2 = y2 - 2 * (cellSize + gap);
|
|
|
|
Region reg, reg2;
|
|
reg = new(new Rectangle(x, y, x2 - x + 1, y2 - y + 1));
|
|
reg.Exclude(new Rectangle(x + 1, y + 1, x2 - x - 1, y2 - y - 1));
|
|
reg2 = new(new Rectangle(xx, yy, xx2 - xx + 1, yy2 - yy + 1));
|
|
reg2.Exclude(new Rectangle(xx + 1, yy + 1, xx2 - xx - 1, yy2 - yy - 1));
|
|
reg.Union(reg2);
|
|
|
|
dotPanel.Invalidate(reg);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Check for Shift / Ctrl keys for straight lines
|
|
if (ModifierKeys.HasFlag(Keys.Shift)) {
|
|
j = lastY;
|
|
} else if (ModifierKeys.HasFlag(Keys.Control)) {
|
|
i = lastX;
|
|
}
|
|
lastX = i;
|
|
lastY = j;
|
|
|
|
// Paint black / white
|
|
if (e.Button == MouseButtons.Left && !f.data[i, j]) {
|
|
f.data[i, j] = true;
|
|
fChanged = true;
|
|
int x = pixelOffset + i * (cellSize + gap) - hScroll.Value;
|
|
int y = pixelOffset + j * (cellSize + gap) - vScroll.Value;
|
|
SetModified();
|
|
rect1 = new Rectangle(x, y, cellSize, cellSize);
|
|
dotPanel.Invalidate(rect1);
|
|
}
|
|
if (e.Button == MouseButtons.Right && f.data[i, j]) {
|
|
f.data[i, j] = false;
|
|
fChanged = true;
|
|
int x = pixelOffset + i * (cellSize + gap) - hScroll.Value;
|
|
int y = pixelOffset + j * (cellSize + gap) - vScroll.Value;
|
|
SetModified();
|
|
rect1 = new Rectangle(x, y, cellSize, cellSize);
|
|
dotPanel.Invalidate(rect1);
|
|
}
|
|
|
|
}
|
|
|
|
private void btnInvert_Click(object sender, EventArgs e) {
|
|
int x, y, x2, y2;
|
|
|
|
(x, y, x2, y2) = RectSelCoords();
|
|
|
|
for (int i = x; i <= x2; i++) {
|
|
for (int j = y; j <= y2; j++) {
|
|
f.data[i, j] = !f.data[i, j];
|
|
}
|
|
}
|
|
|
|
history.Add(f);
|
|
CheckHistoryButtons();
|
|
SetModified();
|
|
dotPanel.Refresh();
|
|
}
|
|
|
|
private void btnMirrorX_Click(object sender, EventArgs e) {
|
|
int a, b, j, x, y, x2, y2;
|
|
bool c;
|
|
|
|
(x, y, x2, y2) = RectSelCoords();
|
|
|
|
for (j = y; j <= y2; j++) {
|
|
a = x;
|
|
b = x2;
|
|
while (a < b) {
|
|
c = f.data[a, j];
|
|
f.data[a, j] = f.data[b, j];
|
|
f.data[b, j] = c;
|
|
a++;
|
|
b--;
|
|
}
|
|
}
|
|
history.Add(f);
|
|
CheckHistoryButtons();
|
|
SetModified();
|
|
dotPanel.Refresh();
|
|
}
|
|
|
|
private void btnMirrorY_Click(object sender, EventArgs e) {
|
|
int a, b, i, x, y, x2, y2;
|
|
bool c;
|
|
|
|
(x, y, x2, y2) = RectSelCoords();
|
|
|
|
for (i = x; i <= x2; i++) {
|
|
a = y;
|
|
b = y2;
|
|
while (a < b) {
|
|
c = f.data[i, a];
|
|
f.data[i, a] = f.data[i, b];
|
|
f.data[i, b] = c;
|
|
a++;
|
|
b--;
|
|
}
|
|
}
|
|
history.Add(f);
|
|
CheckHistoryButtons();
|
|
SetModified();
|
|
dotPanel.Refresh();
|
|
}
|
|
|
|
private void Export_Click(object sender, EventArgs e) {
|
|
if (modified) {
|
|
if (MessageBox.Show("Current symbol is modified.\nDo you want to save the changes?", "Symbol was modified!", MessageBoxButtons.YesNo) == DialogResult.Yes) {
|
|
SaveFrame();
|
|
} else {
|
|
f = CopyFrame(frames.Find(x => x.code == f.code));
|
|
}
|
|
SetModified(false); ;
|
|
}
|
|
|
|
|
|
Export eform = new Export(this);
|
|
eform.ShowDialog();
|
|
}
|
|
|
|
private void button2_Click(object sender, EventArgs e) {
|
|
SaveFrame();
|
|
}
|
|
|
|
private void SaveFrame() {
|
|
int index = frames.FindIndex(x => x.code == f.code);
|
|
frames[index] = f;
|
|
|
|
var sizedBMP = GetMiniPictue(f);
|
|
string s = f.code.ToString().PadLeft(3, '0');
|
|
ilMiniatures.Images.RemoveByKey(s);
|
|
ilMiniatures.Images.Add(s, (Image)sizedBMP);
|
|
sizedBMP.Dispose();
|
|
SetModified(false);
|
|
SetModified(true, true);
|
|
}
|
|
|
|
public static Bitmap GetMiniPictue(FrameMiniature m) {
|
|
int picSize = (m.width > m.height) ? m.width : m.height;
|
|
var bmp = new Bitmap(picSize, picSize);
|
|
int imin = m.width < picSize ? (picSize - m.width) / 2 : 0;
|
|
int jmin = m.height < picSize ? (picSize - m.height) / 2 : 0;
|
|
Color c;
|
|
for (int i = 0; i < m.width; i++) {
|
|
for (int j = 0; j < m.height; j++) {
|
|
c = m.data[i, j] ? Color.Black : Color.White;
|
|
bmp.SetPixel(i + imin, j + jmin, c);
|
|
}
|
|
}
|
|
Bitmap sbmp = new(50, 50);
|
|
using (Graphics g = Graphics.FromImage(sbmp)) {
|
|
g.InterpolationMode = InterpolationMode.NearestNeighbor;
|
|
g.PixelOffsetMode = PixelOffsetMode.Half;
|
|
g.DrawImage(bmp, 0, 0, 50, 50);
|
|
}
|
|
bmp.Dispose();
|
|
return sbmp;
|
|
}
|
|
|
|
private void dotPanel_Paint(object sender, PaintEventArgs e) {
|
|
Graphics g = dotPanel.CreateGraphics();
|
|
SolidBrush sbb = new SolidBrush(Color.Black);
|
|
SolidBrush sbw = new SolidBrush(Color.White);
|
|
SolidBrush sb;
|
|
Pen p = new Pen(Color.FromArgb(100, 20, 120, 20));
|
|
int x, y;
|
|
|
|
// Draw the grid
|
|
for (int i = 0; i < dotWidth; i++) {
|
|
x = pixelOffset + i * (cellSize + gap) - hScroll.Value;
|
|
// Green lines every 8 cells
|
|
if (gap > 0 && i != 0 && (i % 8) == 0) {
|
|
g.DrawLine(p, x - 1, pixelOffset - vScroll.Value, x - 1, h);
|
|
}
|
|
for (int j = 0; j < dotHeight; j++) {
|
|
y = pixelOffset + j * (cellSize + gap) - vScroll.Value;
|
|
// Green lines every 8 cells
|
|
if (gap > 0 && i == 0 && j != 0 && (j % 8) == 0) {
|
|
g.DrawLine(p, pixelOffset - hScroll.Value, y - 1, w, y - 1);
|
|
}
|
|
// Fill the cell with color
|
|
if (f.data[i, j]) sb = sbb;
|
|
else sb = sbw;
|
|
g.FillRectangle(sb, x, (baseline == j ? y + 1 : y), cellSize, (baseline == j ? cellSize - 1 : cellSize));
|
|
}
|
|
}
|
|
|
|
// Draw the baseline
|
|
if (baseline > 0 && gap > 0) {
|
|
x = pixelOffset - hScroll.Value;
|
|
y = pixelOffset + baseline * (cellSize + gap) - vScroll.Value;
|
|
|
|
Pen sbBase = new(Color.Blue, 2);
|
|
g.DrawLine(sbBase, x, y, w, y);
|
|
}
|
|
|
|
// Draw the Rect selection
|
|
if (chkRectSelect.Checked) {
|
|
Point p1 = new(selection1.X, selection1.Y);
|
|
Point p2 = new(selection2.X, selection2.Y);
|
|
NormPoints(ref p1, ref p2);
|
|
|
|
Pen sbRect = new(Color.FromArgb(200, 0, 200, 0), 3);
|
|
x = pixelOffset + p1.X * (cellSize + gap) - hScroll.Value - 1;
|
|
y = pixelOffset + p1.Y * (cellSize + gap) - vScroll.Value - 1;
|
|
int ww = pixelOffset + (p2.X + 1) * (cellSize + gap) - hScroll.Value - x - 1;
|
|
int hh = pixelOffset + (p2.Y + 1) * (cellSize + gap) - vScroll.Value - y - 1;
|
|
g.DrawRectangle(sbRect, x, y, ww, hh);
|
|
}
|
|
}
|
|
|
|
private static void NormPoints(ref Point a, ref Point b) {
|
|
int bot;
|
|
if (a.X > b.X) { bot = a.X; a.X = b.X; b.X = bot; }
|
|
if (a.Y > b.Y) { bot = a.Y; a.Y = b.Y; b.Y = bot; }
|
|
}
|
|
|
|
private void exitToolStripMenuItem_Click(object sender, EventArgs e) {
|
|
Application.Exit();
|
|
}
|
|
|
|
public string DecodeSymbol(int code) {
|
|
var enc = Encoding.GetEncoding(codepage);
|
|
if (code < 32) return "";
|
|
return enc.GetString(new byte[] { (byte)code });
|
|
}
|
|
|
|
private FrameMiniature DrawFrameChar(FrameMiniature ff, Font font, int sx, int sy) {
|
|
string s = DecodeSymbol(ff.code);
|
|
Bitmap bmp = new Bitmap(ff.width, ff.height);
|
|
Graphics g = Graphics.FromImage(bmp);
|
|
g.Clear(Color.White);
|
|
g.SmoothingMode = SmoothingMode.None;
|
|
g.InterpolationMode = InterpolationMode.NearestNeighbor;
|
|
g.PixelOffsetMode = PixelOffsetMode.Half;
|
|
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;
|
|
g.DrawString(s, font, Brushes.Black, sx, sy);
|
|
g.Flush();
|
|
|
|
for (int i = 0; i < ff.width; i++)
|
|
for (int j = 0; j < ff.height; j++) {
|
|
if (bmp.GetPixel(i, j).Name != "ffffffff")
|
|
ff.data[i, j] = true;
|
|
}
|
|
|
|
bmp.Dispose();
|
|
g.Dispose();
|
|
return ff;
|
|
}
|
|
|
|
private void newToolStripMenuItem_Click(object sender, EventArgs e) {
|
|
CheckModifiedFrame();
|
|
if (CheckModifiedProject()) return;
|
|
|
|
New form = new New(this);
|
|
if (form.ShowDialog() == DialogResult.OK) {
|
|
Cursor.Current = Cursors.WaitCursor;
|
|
|
|
frames.Clear();
|
|
miniList.Clear();
|
|
ilMiniatures.Images.Clear();
|
|
|
|
string append = "";
|
|
int neww = (int)form.nudNewX.Value;
|
|
int newh = (int)form.nudNewY.Value;
|
|
nudX.Value = neww;
|
|
nudY.Value = newh;
|
|
FrameMiniature newf;
|
|
if (form.cbSingle.Checked) {
|
|
frames.Add(new FrameMiniature(0, neww, newh));
|
|
append = "Single";
|
|
monospaced = false;
|
|
} else {
|
|
int i, imin, imax;
|
|
if (form.cbDigits.Checked) {
|
|
imin = 48;
|
|
imax = 57;
|
|
} else {
|
|
if (form.cbNotPrintable.Checked) imin = 0;
|
|
else if (form.cbLatin.Checked) imin = 32;
|
|
else imin = 128;
|
|
|
|
if (form.cbExtended.Checked) imax = 255;
|
|
else if (form.cbLatin.Checked) imax = 127;
|
|
else imax = 31;
|
|
}
|
|
|
|
|
|
for (i = imin; i <= imax; i++) {
|
|
newf = new FrameMiniature(i, neww, newh);
|
|
if (form.cbFontBased.Checked) newf = DrawFrameChar(newf, form.dlgFont.Font, (int)form.nudShiftX.Value, (int)form.nudShiftY.Value);
|
|
frames.Add(newf);
|
|
}
|
|
|
|
monospaced = form.rbMono.Checked;
|
|
}
|
|
makeVarWidthToolStripMenuItem.Visible = monospaced;
|
|
tsmiMakeVarWidth.Visible = monospaced;
|
|
CodeShiftToolStripMenuItem.Visible = !form.cbSingle.Checked;
|
|
tsmiCodeShift.Visible = !form.cbSingle.Checked;
|
|
lblType.Text = monospaced ? "Monospaced" : "Variable width / Single";
|
|
codepage = (form.cbEncoding.SelectedItem as New.EncodingItem).Code;
|
|
foreach (FrameMiniature ff in frames) {
|
|
var s = ff.code.ToString().PadLeft(3, '0');
|
|
ilMiniatures.Images.Add(s, (Image)GetMiniPictue(ff));
|
|
var sss = DecodeSymbol(ff.code);
|
|
miniList.Items.Add(s, s + ' ' + append + sss, s);
|
|
}
|
|
f = CopyFrame(frames.First());
|
|
form.Dispose();
|
|
|
|
baseline = 0;
|
|
|
|
prjName = "Untitled";
|
|
prjFileName = "";
|
|
SetWindowCap();
|
|
SetModified(false); ;
|
|
CheckForAdd();
|
|
miniList.Items[0].Selected = true;
|
|
miniList.Refresh();
|
|
dotPanel.Refresh();
|
|
|
|
history.Clear();
|
|
|
|
Cursor.Current = Cursors.Default;
|
|
}
|
|
}
|
|
|
|
private void MiniList_SelectedIndexChanged(object sender, EventArgs e) {
|
|
if (miniList.FocusedItem == null) return;
|
|
CheckModifiedFrame();
|
|
if (miniList.SelectedItems.Count == 0) {
|
|
removeSymbolToolStripMenuItem.Enabled = false;
|
|
removeBeforeToolStripMenuItem.Enabled = false;
|
|
removeAfterToolStripMenuItem.Enabled = false;
|
|
tsmiRemoveSymbol.Enabled = false;
|
|
tsmiRemoveBefore.Enabled = false;
|
|
tsmiRemoveAfter.Enabled = false;
|
|
|
|
return;
|
|
}
|
|
dotPanel.SuspendLayout();
|
|
|
|
var sel = miniList.SelectedItems[0];
|
|
int code = Convert.ToInt32(sel.ImageKey);
|
|
FrameMiniature ff = CopyFrame(frames.Find(x => x.code == code));
|
|
nudX.Value = ff.width;
|
|
nudY.Value = ff.height;
|
|
f = ff;
|
|
|
|
|
|
history.Add(code);
|
|
|
|
ff = frames.Find(x => x.code == code);
|
|
if (frames.Count > 1 && (ff.Equals(frames.First()) || ff.Equals(frames.Last()))) {
|
|
removeSymbolToolStripMenuItem.Enabled = true;
|
|
tsmiRemoveSymbol.Enabled = true;
|
|
} else {
|
|
removeSymbolToolStripMenuItem.Enabled = false;
|
|
tsmiRemoveSymbol.Enabled = false;
|
|
}
|
|
|
|
dotPanel.ResumeLayout();
|
|
dotPanel.Refresh();
|
|
|
|
if (frames.Count > 1 && ff.Equals(frames.First())) {
|
|
removeBeforeToolStripMenuItem.Enabled = false;
|
|
removeAfterToolStripMenuItem.Enabled = true;
|
|
tsmiRemoveBefore.Enabled = false;
|
|
tsmiRemoveAfter.Enabled = true;
|
|
} else if (frames.Count > 1 && ff.Equals(frames.Last())) {
|
|
removeBeforeToolStripMenuItem.Enabled = true;
|
|
removeAfterToolStripMenuItem.Enabled = false;
|
|
tsmiRemoveBefore.Enabled = true;
|
|
tsmiRemoveAfter.Enabled = false;
|
|
} else if (frames.Count > 1) {
|
|
removeBeforeToolStripMenuItem.Enabled = true;
|
|
removeAfterToolStripMenuItem.Enabled = true;
|
|
tsmiRemoveBefore.Enabled = true;
|
|
tsmiRemoveAfter.Enabled = true;
|
|
}
|
|
|
|
}
|
|
|
|
private void SaveToolStripMenuItem_Click(object sender, EventArgs e) {
|
|
CheckModifiedFrame();
|
|
if (dlgSave.ShowDialog() == DialogResult.OK) {
|
|
SaveProject(dlgSave.FileName);
|
|
}
|
|
}
|
|
|
|
public void FillFrameLists() {
|
|
foreach (FrameMiniature f in frames) {
|
|
var s = f.code.ToString().PadLeft(3, '0');
|
|
var sHex = 'x' + Convert.ToString(f.code, 16).PadLeft(2, '0').ToUpper();
|
|
var sss = DecodeSymbol(f.code);
|
|
ilMiniatures.Images.Add(s, (Image)GetMiniPictue(f));
|
|
miniList.Items.Add(s, (chkHexCodes.Checked ? sHex : s) + ' ' + sss, s);
|
|
}
|
|
}
|
|
|
|
private void LoadProject(string filename) {
|
|
SaveBlock sav;
|
|
|
|
Cursor.Current = Cursors.WaitCursor;
|
|
using (FileStream fs = File.Open(filename, FileMode.Open)) {
|
|
sav = MessagePackSerializer.Deserialize<SaveBlock>(fs);
|
|
fs.Close();
|
|
}
|
|
monospaced = sav.monospaced;
|
|
codepage = sav.codepage;
|
|
baseline = sav.baseline;
|
|
lblType.Text = monospaced ? "Monospaced" : "Variable width / Single";
|
|
frames = sav.frames;
|
|
makeVarWidthToolStripMenuItem.Visible = monospaced;
|
|
tsmiMakeVarWidth.Visible = monospaced;
|
|
miniList.Items.Clear();
|
|
ilMiniatures.Images.Clear();
|
|
FillFrameLists();
|
|
nudX.ValueChanged -= nudX_ValueChanged;
|
|
nudY.ValueChanged -= nudY_ValueChanged;
|
|
nudX.Value = frames.First().width;
|
|
nudY.Value = frames.First().height;
|
|
selection1.X = 0; selection1.Y = 0;
|
|
selection2.X = (int)nudX.Value - 1; selection2.Y = (int)nudY.Value - 1;
|
|
DotResize((int)nudX.Value, (int)nudY.Value);
|
|
nudX.ValueChanged += nudX_ValueChanged;
|
|
nudY.ValueChanged += nudY_ValueChanged;
|
|
f = CopyFrame(frames.First());
|
|
dotPanel.Refresh();
|
|
miniList.Refresh();
|
|
SetModified(false);
|
|
SetModified(false, true);
|
|
|
|
prjFileName = filename;
|
|
prjName = Path.GetFileNameWithoutExtension(filename);
|
|
dlgSavePNG.FileName = prjName + ".png";
|
|
dlgSave.FileName = prjName + ".mbfont";
|
|
SetWindowCap();
|
|
|
|
miniList.Items[0].Selected = true;
|
|
|
|
CheckForAdd();
|
|
|
|
history.Clear();
|
|
|
|
tsmiMakeVarWidth.Visible = monospaced;
|
|
makeVarWidthToolStripMenuItem.Visible = monospaced;
|
|
tsmiCodeShift.Visible = frames.Count > 1;
|
|
CodeShiftToolStripMenuItem.Visible = frames.Count > 1;
|
|
Cursor.Current = Cursors.Default;
|
|
}
|
|
|
|
private void SaveProject(string filename) {
|
|
SaveBlock sav;
|
|
sav.monospaced = monospaced;
|
|
sav.frames = frames;
|
|
sav.codepage = codepage;
|
|
sav.baseline = baseline;
|
|
|
|
using (Stream ms = File.OpenWrite(filename)) {
|
|
MessagePackSerializer.Serialize(ms, sav);
|
|
ms.Close();
|
|
}
|
|
|
|
prjName = Path.GetFileNameWithoutExtension(filename);
|
|
prjFileName = filename;
|
|
SetModified(false, true);
|
|
}
|
|
|
|
private void SetWindowCap(string suffix = "") {
|
|
this.Text = "McBitFont v" + version + " - " + prjName + suffix;
|
|
}
|
|
|
|
|
|
|
|
private void openToolStripMenuItem_Click(object sender, EventArgs e) {
|
|
CheckModifiedFrame();
|
|
if (CheckModifiedProject()) return;
|
|
|
|
dlgOpen.FilterIndex = 1;
|
|
if (dlgOpen.ShowDialog() == DialogResult.OK) {
|
|
LoadProject(dlgOpen.FileName);
|
|
}
|
|
}
|
|
|
|
private void removeSymbolToolStripMenuItem_Click(object sender, EventArgs e) {
|
|
if (miniList.SelectedItems.Count == 0) {
|
|
removeSymbolToolStripMenuItem.Enabled = false;
|
|
return;
|
|
}
|
|
var sel = miniList.SelectedItems[0].ImageKey;
|
|
int code = Convert.ToInt32(miniList.SelectedItems[0].ImageKey);
|
|
FrameMiniature ff = frames.Find(x => x.code == code);
|
|
bool isLast = frames.Last().Equals(ff);
|
|
frames.Remove(ff);
|
|
miniList.SelectedItems[0].Remove();
|
|
|
|
miniList.Items[isLast ? miniList.Items.Count - 1 : 0].Selected = true;
|
|
}
|
|
|
|
private void prependSymbolToolStripMenuItem_Click(object sender, EventArgs e) {
|
|
FrameMiniature ff;
|
|
if (sender == prependSymbolToolStripMenuItem || sender == tsmiPrepensSymbol) {
|
|
ff = new FrameMiniature(frames.First().code - 1, dotWidth, dotHeight);
|
|
frames.Insert(0, ff);
|
|
} else {
|
|
ff = new FrameMiniature(frames.Last().code + 1, dotWidth, dotHeight);
|
|
frames.Add(ff);
|
|
}
|
|
|
|
var s = ff.code.ToString().PadLeft(3, '0');
|
|
ilMiniatures.Images.Add(s, (Image)GetMiniPictue(ff));
|
|
var sss = DecodeSymbol(ff.code);
|
|
miniList.Items.Add(s, s + ' ' + sss, s);
|
|
CheckForAdd();
|
|
}
|
|
|
|
private void CheckForAdd() {
|
|
if (frames.Count > 1) {
|
|
if (frames.First().code > 0) {
|
|
prependSymbolToolStripMenuItem.Enabled = true;
|
|
tsmiPrepensSymbol.Enabled = true;
|
|
} else {
|
|
prependSymbolToolStripMenuItem.Enabled = false;
|
|
tsmiPrepensSymbol.Enabled = false;
|
|
}
|
|
if (frames.Last().code < 255) {
|
|
appendSymbolToolStripMenuItem.Enabled = true;
|
|
tsmiAppendSymbol.Enabled = true;
|
|
} else {
|
|
appendSymbolToolStripMenuItem.Enabled = false;
|
|
tsmiAppendSymbol.Enabled = false;
|
|
}
|
|
} else {
|
|
prependSymbolToolStripMenuItem.Enabled = false;
|
|
appendSymbolToolStripMenuItem.Enabled = true;
|
|
tsmiPrepensSymbol.Enabled = false;
|
|
tsmiAppendSymbol.Enabled = true;
|
|
}
|
|
}
|
|
|
|
private void copyToolStripMenuItem_Click(object sender, EventArgs e) {
|
|
var bb = MessagePackSerializer.Serialize(CopyFrame(f, true));
|
|
DataObject clpbObj = new DataObject(clpbFormat.Name, bb);
|
|
Clipboard.SetDataObject(clpbObj, true);
|
|
}
|
|
|
|
private void pasteToolStripMenuItem_Click(object sender, EventArgs e) {
|
|
// Try to read from clipboard
|
|
try {
|
|
IDataObject clpbObj = Clipboard.GetDataObject();
|
|
byte[] bb = (byte[])clpbObj.GetData(clpbFormat.Name);
|
|
fbuf = MessagePackSerializer.Deserialize<FrameMiniature>(bb);
|
|
}
|
|
catch {
|
|
return;
|
|
}
|
|
|
|
int di, dj, wmax, hmax, selw, selh;
|
|
if (chkRectSelect.Checked) {
|
|
di = selection1.X;
|
|
dj = selection1.Y;
|
|
selw = selection2.X - selection1.X + 1;
|
|
selh = selection2.Y - selection1.Y + 1;
|
|
wmax = fbuf.width > selw ? selw : fbuf.width;
|
|
hmax = fbuf.height > selh ? selh : fbuf.height;
|
|
} else {
|
|
di = 0;
|
|
dj = 0;
|
|
wmax = (fbuf.width > f.width) ? f.width : fbuf.width;
|
|
hmax = (fbuf.height > f.height) ? f.height : fbuf.height;
|
|
}
|
|
|
|
for (int i = 0; i < wmax; i++) {
|
|
for (int j = 0; j < hmax; j++) {
|
|
f.data[i + di, j + dj] = fbuf.data[i, j];
|
|
}
|
|
}
|
|
|
|
history.Add(f);
|
|
CheckHistoryButtons();
|
|
dotPanel.Refresh();
|
|
SetModified();
|
|
}
|
|
|
|
private void aboutToolStripMenuItem_Click(object sender, EventArgs e) {
|
|
About form = new About();
|
|
form.ShowDialog();
|
|
}
|
|
|
|
private void btnBaseline_Click(object sender, EventArgs e) {
|
|
set_base = !set_base;
|
|
}
|
|
|
|
// Check modified / Save frame
|
|
private void CheckModifiedFrame() {
|
|
if (modified) {
|
|
if (MessageBox.Show("Current symbol is modified.\nDo you want to save the changes?", "Symbol was modified!", MessageBoxButtons.YesNo) == DialogResult.Yes) {
|
|
SaveFrame();
|
|
}
|
|
SetModified(false);
|
|
}
|
|
}
|
|
|
|
// Check if project was modified
|
|
private bool CheckModifiedProject() {
|
|
if (prjModified) {
|
|
if (MessageBox.Show("The project is modified.\nDo you want to save it first?", "Project was modified!", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) {
|
|
saveToolStripMenuItem.PerformClick();
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private void saveToolStripMenuItem_Click_1(object sender, EventArgs e) {
|
|
if (prjFileName == "") {
|
|
saveAsToolStripMenuItem.PerformClick();
|
|
} else {
|
|
CheckModifiedFrame();
|
|
SaveProject(prjFileName);
|
|
}
|
|
|
|
}
|
|
|
|
private void FillFrame(bool val) {
|
|
int x, y, x2, y2;
|
|
|
|
(x, y, x2, y2) = RectSelCoords();
|
|
|
|
for (int i = x; i <= x2; i++) {
|
|
for (int j = y; j <= y2; j++) {
|
|
f.data[i, j] = val;
|
|
}
|
|
}
|
|
|
|
history.Add(f);
|
|
CheckHistoryButtons();
|
|
SetModified();
|
|
dotPanel.Refresh();
|
|
}
|
|
|
|
private void btnClear_Click(object sender, EventArgs e) {
|
|
FillFrame(false);
|
|
}
|
|
|
|
private void btnFill_Click(object sender, EventArgs e) {
|
|
FillFrame(true);
|
|
}
|
|
|
|
private void MainForm_FormClosing(object sender, FormClosingEventArgs e) {
|
|
CheckModifiedFrame();
|
|
if (prjModified) {
|
|
if (MessageBox.Show("The project is modified.\nAre you sure you want to quit?", "Are you sure?", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No) {
|
|
e.Cancel = true;
|
|
} else {
|
|
e.Cancel = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void scroll_ValueChanged(object sender, EventArgs e) {
|
|
dotPanel.Refresh();
|
|
}
|
|
|
|
// Remove all before or after specified symbol
|
|
private void removeBA(bool before) {
|
|
if (miniList.SelectedItems.Count == 0) {
|
|
removeBeforeToolStripMenuItem.Enabled = false;
|
|
removeAfterToolStripMenuItem.Enabled = false;
|
|
return;
|
|
}
|
|
int step = before ? -1 : 1;
|
|
var sel = miniList.SelectedItems[0].ImageKey;
|
|
int code = Convert.ToInt32(sel);
|
|
int findex;
|
|
|
|
while (miniList.Items.ContainsKey((code + step).ToString().PadLeft(3, '0'))) {
|
|
code += step;
|
|
findex = frames.FindIndex(x => x.code == code);
|
|
frames.RemoveAt(findex);
|
|
miniList.Items.RemoveByKey(code.ToString().PadLeft(3, '0'));
|
|
}
|
|
//dotPanel.Refresh();
|
|
miniList.Refresh();
|
|
SetModified(true, true);
|
|
}
|
|
|
|
private void removeBeforeToolStripMenuItem_Click(object sender, EventArgs e) {
|
|
removeBA(true);
|
|
}
|
|
|
|
private void removeAfterToolStripMenuItem_Click(object sender, EventArgs e) {
|
|
removeBA(false);
|
|
}
|
|
|
|
private void makeVarWidthToolStripMenuItem_Click(object sender, EventArgs e) {
|
|
monospaced = false;
|
|
makeVarWidthToolStripMenuItem.Visible = false;
|
|
tsmiMakeVarWidth.Visible = false;
|
|
lblType.Text = "Variable width / Single";
|
|
SetModified(true, true);
|
|
}
|
|
|
|
public void CheckHistoryButtons() {
|
|
undoToolStripMenuItem.Enabled = history.Undos > 0;
|
|
redoToolStripMenuItem.Enabled = history.Redos > 0;
|
|
|
|
undoToolStripMenuItem.Text = "Undo (" + history.Undos + ")";
|
|
redoToolStripMenuItem.Text = "Redo (" + history.Redos + ")";
|
|
}
|
|
|
|
private void editToolStripMenuItem_DropDownOpening(object sender, EventArgs e) {
|
|
CheckHistoryButtons();
|
|
}
|
|
|
|
private void undoToolStripMenuItem_Click(object sender, EventArgs e) {
|
|
history.Undo();
|
|
dotPanel.Refresh();
|
|
CheckHistoryButtons();
|
|
}
|
|
|
|
private void redoToolStripMenuItem_Click(object sender, EventArgs e) {
|
|
history.Redo();
|
|
dotPanel.Refresh();
|
|
CheckHistoryButtons();
|
|
}
|
|
|
|
private void CodeShiftToolStripMenuItem_Click(object sender, EventArgs e) {
|
|
if (!monospaced && frames.Count == 1) return;
|
|
CheckModifiedFrame();
|
|
|
|
CodeShift csform = new CodeShift(this);
|
|
if (csform.ShowDialog() == DialogResult.OK) {
|
|
Cursor.Current = Cursors.WaitCursor;
|
|
|
|
FrameMiniature ff;
|
|
int sel;
|
|
if (miniList.SelectedItems.Count > 0) sel = miniList.SelectedItems[0].Index;
|
|
else sel = 0;
|
|
var val = Convert.ToInt32(csform.nudValue.Value);
|
|
if (csform.rbSpecify.Checked) val -= csform.sc;
|
|
if (csform.rbShiftLeft.Checked) val *= -1;
|
|
|
|
miniList.Clear();
|
|
ilMiniatures.Images.Clear();
|
|
for (int i = 0; i < frames.Count; i++) {
|
|
ff = frames[i];
|
|
ff.code += val;
|
|
frames[i] = ff;
|
|
|
|
var key = ff.code.ToString().PadLeft(3, '0');
|
|
var text = DecodeSymbol(ff.code);
|
|
ilMiniatures.Images.Add(key, (Image)GetMiniPictue(ff));
|
|
miniList.Items.Add(key, key + ' ' + text, key);
|
|
}
|
|
|
|
miniList.Refresh();
|
|
miniList.Items[sel].Selected = true;
|
|
dotPanel.Refresh();
|
|
|
|
Cursor.Current = Cursors.Default;
|
|
}
|
|
csform.Dispose();
|
|
|
|
history.Clear();
|
|
CheckForAdd();
|
|
SetModified(true, true);
|
|
|
|
}
|
|
|
|
private void importImageToolStripMenuItem_Click(object sender, EventArgs e) {
|
|
ImageImporter iform = new ImageImporter(f.width, f.height);
|
|
if (iform.ShowDialog() == DialogResult.OK) {
|
|
for (int i = 0; i < iform.bmpScaled.Width; i++) {
|
|
for (int j = 0; j < iform.bmpScaled.Height; j++) {
|
|
f.data[i, j] = iform.bmpScaled.GetPixel(i, j).ToArgb().Equals(Color.Black.ToArgb());
|
|
}
|
|
}
|
|
history.Add(f);
|
|
CheckHistoryButtons();
|
|
dotPanel.Refresh();
|
|
SetModified();
|
|
}
|
|
iform.Dispose();
|
|
}
|
|
|
|
private void chkHexCodes_CheckedChanged(object sender, EventArgs e) {
|
|
if (frames.Count == 1) return;
|
|
foreach (ListViewItem item in miniList.Items) {
|
|
var code = Convert.ToInt32(item.ImageKey);
|
|
var symbol = DecodeSymbol(code);
|
|
if (chkHexCodes.Checked) {
|
|
var sHex = 'x' + Convert.ToString(code, 16).PadLeft(2, '0').ToUpper();
|
|
item.Text = sHex + ' ' + symbol;
|
|
} else {
|
|
item.Text = item.ImageKey + ' ' + symbol;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
private void chkRectSelect_CheckedChanged(object sender, EventArgs e) {
|
|
lblSelection.Visible = lblSelectionLabel.Visible = chkRectSelect.Checked;
|
|
selectAllToolStripMenuItem.Enabled = chkRectSelect.Checked;
|
|
dotPanel.Refresh();
|
|
}
|
|
|
|
private void selectToolStripMenuItem_Click(object sender, EventArgs e) {
|
|
chkRectSelect.Checked = !chkRectSelect.Checked;
|
|
selectToolStripMenuItem.Checked = !selectToolStripMenuItem.Checked;
|
|
}
|
|
|
|
private void importTextToolStripMenuItem1_Click(object sender, EventArgs e) {
|
|
if (CheckModifiedProject()) return;
|
|
|
|
dlgOpen.FilterIndex = 2;
|
|
if (dlgOpen.ShowDialog() == DialogResult.OK) {
|
|
var lines = File.ReadAllLines(dlgOpen.FileName);
|
|
int bits = 0;
|
|
List<uint> data = [];
|
|
foreach (var line in lines) {
|
|
if (line.TrimStart().StartsWith("//")) continue; // Ignore comments
|
|
if (line.Trim().Length < 1) continue; // Ignore empty lines
|
|
if (bits == 0) {
|
|
if (line.Contains("uint8_t")) bits = 8; // Found 8 bit array
|
|
if (line.Contains("uint16_t")) bits = 16; // Found 16 bit array
|
|
if (line.Contains("uint31_t")) bits = 32; // Found 32 bit array
|
|
}
|
|
string s = line;
|
|
if (s.Contains("//")) s = s[..s.IndexOf("//")]; // Remove comments
|
|
if (s.Contains('{')) s = s[(s.IndexOf('{') + 1)..]; // Remove everything before '{'
|
|
|
|
var words = s.Split(','); // split a line by commas
|
|
string w;
|
|
foreach (var word in words) {
|
|
int numBase = 10;
|
|
|
|
w = word.Trim();
|
|
if (w.Length < 1) continue;
|
|
|
|
if (w.Contains("0b") && !w.Contains("0x")) { // Check if value is written as binary
|
|
w = w.Remove(w.IndexOf("0b"), 2);
|
|
numBase = 2;
|
|
}
|
|
if (w.Contains("0x")) { // Check if value is written as hexadecimal
|
|
w = w.Remove(w.IndexOf("0x"), 2);
|
|
numBase = 16;
|
|
}
|
|
|
|
|
|
try { // Try to convert a number from text
|
|
data.Add(Convert.ToUInt32(w, numBase));
|
|
}
|
|
catch {
|
|
continue;
|
|
}
|
|
|
|
//MessageBox.Show(w + ": Length: " + w.Length + " Bits: " + bits + " Converted: " + data.Last() + "\nData length: " + data.Count);
|
|
}
|
|
}
|
|
if (MessageBox.Show(bits + "-bit font found. " + data.Count + " numbers (" + data.Count * bits / 8 + " bytes) total\n" + "Start code: " + data.ElementAt(4) + " End code: " + data.ElementAt(5) + "\nDo you want to load it?", "Import from text file", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) {
|
|
// Font header
|
|
bool packed = data.ElementAt(0) == 1;
|
|
int width = (int)data.ElementAt(1);
|
|
int height = (int)data.ElementAt(2);
|
|
int first = (int)data.ElementAt(4);
|
|
int last = (int)data.ElementAt(5);
|
|
|
|
frames.Clear();
|
|
miniList.Clear();
|
|
ilMiniatures.Images.Clear();
|
|
|
|
FrameMiniature newf = new();
|
|
int neww = width;
|
|
int curFrame = -1;
|
|
bool newFrame = true;
|
|
int i = 0, j = 0;
|
|
for (int b = 6; b < data.Count; b++) {
|
|
if (newFrame) {
|
|
newFrame = false;
|
|
curFrame++;
|
|
i = 0; j = 0;
|
|
if (width == 0) neww = (int)data[b];
|
|
newf = new(curFrame + first, neww, height);
|
|
if (width == 0 && neww != 0) continue;
|
|
}
|
|
// Fill frame data with current number
|
|
if (neww != 0)
|
|
for (int bit = 0; bit < bits; bit++) {
|
|
if (j >= height) {
|
|
j = 0;
|
|
i++;
|
|
if (!packed && bit != 0) break;
|
|
}
|
|
if (i >= neww) break;
|
|
newf.data[i, j] = (data[b] & (1 << bit)) > 0;
|
|
j++;
|
|
}
|
|
if (i >= neww || b == data.Count - 1 || (i == neww - 1 && j == height) || neww == 0) {
|
|
newFrame = true;
|
|
frames.Add(newf);
|
|
|
|
var s = (last - first > 0) ? newf.code.ToString().PadLeft(3, '0') : "000";
|
|
var sHex = (last - first > 0) ? 'x' + Convert.ToString(newf.code, 16).PadLeft(2, '0').ToUpper() : "0x0";
|
|
var sss = (last - first > 0) ? DecodeSymbol(newf.code) : "Single";
|
|
ilMiniatures.Images.Add(s, (Image)GetMiniPictue(newf));
|
|
miniList.Items.Add(s, (chkHexCodes.Checked ? sHex : s) + ' ' + sss, s);
|
|
}
|
|
}
|
|
SetModified(true, true);
|
|
if (miniList.Items.Count > 0) miniList.Items[0].Selected = true;
|
|
f = frames[0];
|
|
dotWidth = f.width;
|
|
dotHeight = f.height;
|
|
SetNewWH();
|
|
|
|
miniList.Refresh();
|
|
dotPanel.Refresh();
|
|
}
|
|
//MessageBox.Show(bits + "-font found. " + data.Count + " numbers (" + data.Count * bits / 8 + " bytes) total\n" + "Start code: " + data.ElementAt(4) + " End code: " + data.ElementAt(5));
|
|
}
|
|
}
|
|
|
|
private void selectAllToolStripMenuItem_Click(object sender, EventArgs e) {
|
|
selection1.X = 0;
|
|
selection1.Y = 0;
|
|
selection2.X = dotWidth - 1;
|
|
selection2.Y = dotHeight - 1;
|
|
dotPanel.Refresh();
|
|
}
|
|
|
|
private void TestFont_Click(object sender, EventArgs e) {
|
|
var tester = new FontTester(codepage, dotHeight, baseline, frames);
|
|
tester.ShowDialog();
|
|
}
|
|
|
|
private void ExportPNG(object sender, EventArgs e) {
|
|
CheckModifiedFrame();
|
|
CheckModifiedProject();
|
|
|
|
if (dlgSavePNG.ShowDialog() == DialogResult.OK) {
|
|
int pixelSize = 3;
|
|
int symbolMargin = 2 * pixelSize;
|
|
int headerHeight = 100;
|
|
int maxWidth = 0;
|
|
int i, j, v, h, x, y;
|
|
string s;
|
|
|
|
foreach (FrameMiniature f in frames) {
|
|
if (f.width > maxWidth) maxWidth = f.width;
|
|
}
|
|
|
|
int cellWidth = (pixelSize * maxWidth + 2 * symbolMargin);
|
|
int cellHeight = (pixelSize * dotHeight + 2 * symbolMargin);
|
|
if (cellWidth < 10) cellWidth = 10;
|
|
if (cellHeight < 10) cellHeight = 10;
|
|
|
|
int width = cellWidth * 16 + 17 + 50;
|
|
int height = cellHeight * 16 + 17 + 25 + headerHeight;
|
|
|
|
Bitmap bmp = new(width > 450 ? width : 450, height);
|
|
Graphics g = Graphics.FromImage(bmp);
|
|
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
|
|
Font font = new("Consolas", 14, FontStyle.Bold);
|
|
Brush tb = Brushes.Black;
|
|
|
|
|
|
// Draw basic information
|
|
g.DrawString("Font project name: " + prjName, font, tb, 10, 10);
|
|
|
|
g.DrawString("Font height: " + dotHeight, font, tb, 10, 38);
|
|
g.DrawString("Font max width: " + maxWidth, font, tb, 10, 62);
|
|
g.DrawString("Symbols count: " + frames.Count, font, tb, 10, 86);
|
|
|
|
g.DrawString("First code: " + frames.First().code.ToString(), font, tb, 250, 38);
|
|
g.DrawString("Last code: " + frames.Last().code.ToString(), font, tb, 250, 62);
|
|
g.DrawString("Codepage: " + codepage.ToString(), font, tb, 250, 86);
|
|
|
|
|
|
// Draw grid
|
|
Pen p = new(Color.FromArgb(64, 0, 0, 0), 1);
|
|
SolidBrush b = new SolidBrush(Color.FromArgb(160, Color.Black));
|
|
//Brush b = Brushes.Black;
|
|
|
|
int xCapOffset = cellWidth / 2 - 8;
|
|
int yCapOffset = cellHeight / 2 - 10;
|
|
for (i = 0; i < 17; i++) {
|
|
x = 50 + 17 + i * cellWidth - 1;
|
|
y = headerHeight + 17 + 25 + i * cellHeight - 1;
|
|
g.DrawLine(p, 1, y, width - 1, y);
|
|
g.DrawLine(p, x, headerHeight + 20, x, headerHeight + height - 20);
|
|
|
|
if (i != 16) {
|
|
s = Convert.ToString(i, 16).ToUpper();
|
|
g.DrawString(s, font, b, x + xCapOffset, headerHeight + 20);
|
|
s = "0x" + Convert.ToString(i * 16, 16).PadLeft(2, '0').ToUpper();
|
|
g.DrawString(s, font, b, 10, y + yCapOffset);
|
|
}
|
|
}
|
|
|
|
// Draw symbols
|
|
int code;
|
|
List<FrameMiniature> ff;
|
|
Rectangle rect;
|
|
// Cycle through grid cells
|
|
for (v = 0; v < 16; v++) {
|
|
for (h = 0; h < 16; h++) {
|
|
// Check if the font has a symbol with the code
|
|
code = 16 * v + h;
|
|
ff = frames.FindAll(x => x.code == code);
|
|
if (ff.Count == 1) {
|
|
x = 50 + 17 + h * cellWidth + symbolMargin;
|
|
y = headerHeight + 17 + 25 + v * cellHeight + symbolMargin;
|
|
// Cycly through symbol's pixels and draw the black ones
|
|
for (i = 0; i < ff[0].width; i++) {
|
|
for (j = 0; j < ff[0].height; j++) {
|
|
if (ff[0].data[i, j]) {
|
|
rect = new Rectangle(x + i * pixelSize, y + j * pixelSize, pixelSize, pixelSize);
|
|
g.FillRectangle(tb, rect);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bmp.Save(dlgSavePNG.FileName, ImageFormat.Png);
|
|
}
|
|
|
|
}
|
|
|
|
private void toggleBarToolStripMenuItem_Click(object sender, EventArgs e) {
|
|
int state;
|
|
|
|
dotPanel.Width = this.Width - (toggleBarToolStripMenuItem.Checked ? 260 : 504);
|
|
SideBarRecalc();
|
|
if (toggleBarToolStripMenuItem.Checked) {
|
|
toggleBarToolStripMenuItem.Text = "<<";
|
|
state = 1;
|
|
} else {
|
|
toggleBarToolStripMenuItem.Text = ">>";
|
|
state = 0;
|
|
}
|
|
panel1.Location = sidebarLocs[state, 0];
|
|
pnlInfo.Location = sidebarLocs[state, 1];
|
|
miniList.Visible = !toggleBarToolStripMenuItem.Checked;
|
|
vScroll.Location = sidebarLocs[state, 2];
|
|
hScroll.Width = dotPanel.Width;
|
|
cbZoom.Focus();
|
|
}
|
|
|
|
private void PrevNextMenuCheck() {
|
|
if (frames.Count < 2) {
|
|
nextSymbolToolStripMenuItem.Enabled = false;
|
|
previousSymbolToolStripMenuItem.Enabled = false;
|
|
return;
|
|
}
|
|
previousSymbolToolStripMenuItem.Enabled = f.code != frames.First().code;
|
|
nextSymbolToolStripMenuItem.Enabled = f.code != frames.Last().code;
|
|
}
|
|
private void fontToolStripMenuItem_DropDownOpening(object sender, EventArgs e) {
|
|
PrevNextMenuCheck();
|
|
}
|
|
|
|
private void previousSymbolToolStripMenuItem_Click(object sender, EventArgs e) {
|
|
if (f.code != frames.First().code && miniList.SelectedItems.Count > 0) {
|
|
miniList.Items[miniList.SelectedIndices[0] - 1].Selected = true;
|
|
}
|
|
PrevNextMenuCheck();
|
|
}
|
|
|
|
private void nextSymbolToolStripMenuItem_Click(object sender, EventArgs e) {
|
|
if (f.code != frames.Last().code && miniList.SelectedItems.Count > 0) {
|
|
miniList.Items[miniList.SelectedIndices[0] + 1].Selected = true;
|
|
}
|
|
PrevNextMenuCheck();
|
|
}
|
|
|
|
private void ZerofyBlankWidth(object sender, EventArgs e) {
|
|
if (monospaced) return; // Does not work for monospaced fonts
|
|
if (frames.Count < 2) return; // Does not work for single images
|
|
|
|
for (int i = 0; i < frames.Count; i++) {
|
|
if (IsFrameBlank(frames[i])) {
|
|
Bitmap bmp;
|
|
|
|
frames[i] = FrameResize(frames[i], 0, dotHeight);
|
|
bmp = GetMiniPictue(frames[i]);
|
|
string s = frames[i].code.ToString().PadLeft(3, '0');
|
|
ilMiniatures.Images.RemoveByKey(s);
|
|
ilMiniatures.Images.Add(s, (Image)bmp);
|
|
miniList.Items[s].ImageKey = s;
|
|
|
|
SetModified(true, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|