Initial commit: OtozaiPrinter (AveryDennisonUtil) ラベル/レシート レイアウト設計・印刷 MFC アプリ
- 用紙設計キャンバス(1/100mm 基準)・テキスト/画像/矩形/直線アイテム - コピー/複製/Zオーダー/整列/グリッドスナップ、原紙(下絵)読込と印刷可否 - GDI 直接印刷、.otz 保存/再読込(版数つき) - MFC/CRT 静的リンクで VS2022 IDE から F5 デバッグ可
This commit is contained in:
+422
@@ -0,0 +1,422 @@
|
||||
// MainDialog.cpp
|
||||
#include "pch.h"
|
||||
#include "AveryDennisonUtil.h"
|
||||
#include "MainDialog.h"
|
||||
#include "RenderScene.h"
|
||||
#include "PaperSizeDlg.h"
|
||||
|
||||
// ===== バージョン情報ダイアログ =====
|
||||
class CAboutDlg : public CDialog
|
||||
{
|
||||
public:
|
||||
CAboutDlg() : CDialog(IDD_ABOUTBOX) {}
|
||||
enum { IDD = IDD_ABOUTBOX };
|
||||
protected:
|
||||
void DoDataExchange(CDataExchange* pDX) override { CDialog::DoDataExchange(pDX); }
|
||||
DECLARE_MESSAGE_MAP()
|
||||
};
|
||||
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
// 子キャンバスのウィンドウ ID(リソース上の IDC_CANVAS とは別)
|
||||
static const UINT kCanvasId = 1101;
|
||||
|
||||
static LPCTSTR kFilter =
|
||||
_T("Otozai ラベル (*.otz)|*.otz|すべてのファイル (*.*)|*.*||");
|
||||
|
||||
BEGIN_MESSAGE_MAP(CMainDialog, CDialog)
|
||||
ON_COMMAND(ID_FILE_NEW, &CMainDialog::OnFileNew)
|
||||
ON_COMMAND(ID_FILE_OPEN, &CMainDialog::OnFileOpen)
|
||||
ON_COMMAND(ID_FILE_SAVE, &CMainDialog::OnFileSave)
|
||||
ON_COMMAND(ID_FILE_SAVE_AS, &CMainDialog::OnFileSaveAs)
|
||||
ON_COMMAND(ID_INSERT_TEXT, &CMainDialog::OnInsertText)
|
||||
ON_COMMAND(ID_INSERT_IMAGE, &CMainDialog::OnInsertImage)
|
||||
ON_COMMAND(ID_INSERT_RECT, &CMainDialog::OnInsertRect)
|
||||
ON_COMMAND(ID_INSERT_LINE, &CMainDialog::OnInsertLine)
|
||||
ON_COMMAND(ID_EDIT_PROPERTIES, &CMainDialog::OnEditProperties)
|
||||
ON_COMMAND(ID_EDIT_DELETE_ITEM, &CMainDialog::OnEditDeleteItem)
|
||||
ON_COMMAND(ID_PAPER_SIZE, &CMainDialog::OnPaperSize)
|
||||
ON_COMMAND(ID_VIEW_ZOOMIN, &CMainDialog::OnViewZoomIn)
|
||||
ON_COMMAND(ID_VIEW_ZOOMOUT, &CMainDialog::OnViewZoomOut)
|
||||
ON_COMMAND(ID_FILE_PRINT, &CMainDialog::OnFilePrint)
|
||||
ON_COMMAND(ID_APP_ABOUT, &CMainDialog::OnAppAbout)
|
||||
ON_COMMAND(ID_ITEM_COPY, &CMainDialog::OnItemCopy)
|
||||
ON_COMMAND(ID_ITEM_PASTE, &CMainDialog::OnItemPaste)
|
||||
ON_COMMAND(ID_ITEM_DUPLICATE, &CMainDialog::OnItemDuplicate)
|
||||
ON_COMMAND(ID_ITEM_FRONT, &CMainDialog::OnItemFront)
|
||||
ON_COMMAND(ID_ITEM_BACK, &CMainDialog::OnItemBack)
|
||||
ON_COMMAND(ID_ALIGN_MENU, &CMainDialog::OnAlignMenu)
|
||||
ON_COMMAND_RANGE(ID_ALIGN_LEFT, ID_ALIGN_BOTTOM, &CMainDialog::OnAlignCmd)
|
||||
ON_BN_CLICKED(IDC_CHECK_GRID, &CMainDialog::OnToggleGrid)
|
||||
ON_COMMAND(ID_TEMPLATE_LOAD, &CMainDialog::OnTemplateLoad)
|
||||
ON_COMMAND(ID_TEMPLATE_CLEAR, &CMainDialog::OnTemplateClear)
|
||||
ON_BN_CLICKED(IDC_CHECK_PRINT_TEMPLATE, &CMainDialog::OnTogglePrintTemplate)
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
// コンストラクタ。メインダイアログのリソース ID を指定して生成。
|
||||
CMainDialog::CMainDialog(CWnd* pParent)
|
||||
: CDialog(IDD_MAIN_DIALOG, pParent)
|
||||
{
|
||||
}
|
||||
|
||||
// コントロールと変数の関連付け(本ダイアログは直接の DDX 変数は持たない)。
|
||||
void CMainDialog::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
}
|
||||
|
||||
// ダイアログ初期化。プレースホルダ位置に編集キャンバスを生成し、
|
||||
// チェックボックスの初期状態をモデルへ合わせる。
|
||||
BOOL CMainDialog::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
SetIcon(AfxGetApp()->LoadStandardIcon(IDI_APPLICATION), TRUE);
|
||||
SetIcon(AfxGetApp()->LoadStandardIcon(IDI_APPLICATION), FALSE);
|
||||
|
||||
// プレースホルダ(IDC_CANVAS)の矩形にキャンバスを生成
|
||||
CWnd* pHolder = GetDlgItem(IDC_CANVAS);
|
||||
CRect rc;
|
||||
if (pHolder)
|
||||
{
|
||||
pHolder->GetWindowRect(rc);
|
||||
ScreenToClient(rc);
|
||||
pHolder->ShowWindow(SW_HIDE);
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = CRect(80, 10, 460, 300);
|
||||
}
|
||||
|
||||
m_canvas.SetLayout(&m_layout);
|
||||
m_canvas.Create(this, rc, kCanvasId);
|
||||
m_canvas.FitZoom();
|
||||
|
||||
// グリッド/スナップのチェック状態をキャンバスの既定に合わせる
|
||||
CButton* pGrid = (CButton*)GetDlgItem(IDC_CHECK_GRID);
|
||||
if (pGrid) pGrid->SetCheck(m_canvas.GetSnap() ? BST_CHECKED : BST_UNCHECKED);
|
||||
|
||||
UpdateTemplateCheck();
|
||||
UpdateCaption();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// タイトルバーを「ファイル名 - AveryDennisonUtil」に更新する(未保存は「無題」)。
|
||||
void CMainDialog::UpdateCaption()
|
||||
{
|
||||
CString name = m_path.IsEmpty() ? CString(_T("無題")) : m_path;
|
||||
if (!m_path.IsEmpty())
|
||||
{
|
||||
int slash = m_path.ReverseFind(_T('\\'));
|
||||
if (slash >= 0) name = m_path.Mid(slash + 1);
|
||||
}
|
||||
CString title;
|
||||
title.Format(_T("%s - AveryDennisonUtil"), (LPCTSTR)name);
|
||||
SetWindowText(title);
|
||||
}
|
||||
|
||||
// 未保存の変更がある場合に保存確認する。続行可なら TRUE、キャンセルなら FALSE。
|
||||
// 新規/開く/終了の前に呼んで作業内容の消失を防ぐ。
|
||||
BOOL CMainDialog::ConfirmDiscardIfDirty()
|
||||
{
|
||||
if (!m_canvas.IsDirty())
|
||||
return TRUE;
|
||||
int r = AfxMessageBox(_T("変更を保存しますか?"), MB_YESNOCANCEL | MB_ICONQUESTION);
|
||||
if (r == IDCANCEL) return FALSE;
|
||||
if (r == IDYES) return DoSave(FALSE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Enter キーでダイアログが閉じないように無効化(編集中の誤終了防止)。
|
||||
void CMainDialog::OnOK()
|
||||
{
|
||||
// Enter キーでダイアログを閉じない(編集を継続)
|
||||
}
|
||||
|
||||
// 終了(×ボタン/Esc/「終了」)。未保存なら確認してから閉じる。
|
||||
void CMainDialog::OnCancel()
|
||||
{
|
||||
if (!ConfirmDiscardIfDirty())
|
||||
return;
|
||||
CDialog::OnCancel();
|
||||
}
|
||||
|
||||
// ===== ファイル =====
|
||||
|
||||
// 「新規」。未保存確認の後、レイアウトを初期化して表示を更新する。
|
||||
void CMainDialog::OnFileNew()
|
||||
{
|
||||
if (!ConfirmDiscardIfDirty()) return;
|
||||
m_layout.Reset();
|
||||
m_path.Empty();
|
||||
m_canvas.OnLayoutReplaced();
|
||||
m_canvas.FitZoom();
|
||||
UpdateTemplateCheck();
|
||||
UpdateCaption();
|
||||
}
|
||||
|
||||
// 「開く」。.otz を選んで読み込み、表示・チェック状態・タイトルを更新する。
|
||||
void CMainDialog::OnFileOpen()
|
||||
{
|
||||
if (!ConfirmDiscardIfDirty()) return;
|
||||
CFileDialog dlg(TRUE, _T("otz"), nullptr,
|
||||
OFN_FILEMUSTEXIST | OFN_HIDEREADONLY, kFilter, this);
|
||||
if (dlg.DoModal() != IDOK) return;
|
||||
|
||||
if (!m_layout.LoadFromFile(dlg.GetPathName()))
|
||||
{
|
||||
AfxMessageBox(_T("ファイルを開けませんでした。"));
|
||||
m_layout.Reset();
|
||||
}
|
||||
m_path = dlg.GetPathName();
|
||||
m_canvas.OnLayoutReplaced();
|
||||
m_canvas.FitZoom();
|
||||
UpdateTemplateCheck();
|
||||
UpdateCaption();
|
||||
}
|
||||
|
||||
void CMainDialog::OnFileSave() { DoSave(FALSE); } // 「保存」(既存パスへ上書き)
|
||||
void CMainDialog::OnFileSaveAs(){ DoSave(TRUE); } // 「名前を付けて保存」
|
||||
|
||||
// 保存の実処理。bForceDialog=TRUE またはパス未設定のときは保存ダイアログを出す。
|
||||
// 成功で TRUE。保存後はダーティ状態を解除しタイトルを更新する。
|
||||
BOOL CMainDialog::DoSave(BOOL bForceDialog)
|
||||
{
|
||||
CString path = m_path;
|
||||
if (bForceDialog || path.IsEmpty())
|
||||
{
|
||||
CFileDialog dlg(FALSE, _T("otz"), m_path.IsEmpty() ? _T("label.otz") : (LPCTSTR)m_path,
|
||||
OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY, kFilter, this);
|
||||
if (dlg.DoModal() != IDOK) return FALSE;
|
||||
path = dlg.GetPathName();
|
||||
}
|
||||
if (!m_layout.SaveToFile(path))
|
||||
{
|
||||
AfxMessageBox(_T("保存に失敗しました。"));
|
||||
return FALSE;
|
||||
}
|
||||
m_path = path;
|
||||
m_canvas.SetDirty(FALSE);
|
||||
UpdateCaption();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// ===== 挿入・編集(キャンバスへ委譲) =====
|
||||
|
||||
void CMainDialog::OnInsertText() { m_canvas.InsertText(); }
|
||||
void CMainDialog::OnInsertImage() { m_canvas.InsertImage(); }
|
||||
void CMainDialog::OnInsertRect() { m_canvas.InsertRect(); }
|
||||
void CMainDialog::OnInsertLine() { m_canvas.InsertLine(); }
|
||||
void CMainDialog::OnEditProperties() { m_canvas.EditSelectedProperties(); }
|
||||
void CMainDialog::OnEditDeleteItem() { m_canvas.DeleteSelected(); }
|
||||
void CMainDialog::OnViewZoomIn() { m_canvas.ZoomIn(); }
|
||||
void CMainDialog::OnViewZoomOut() { m_canvas.ZoomOut(); }
|
||||
|
||||
// 「用紙サイズ」。現在値をダイアログに渡し、OK なら用紙寸法・DPI・余白を反映する。
|
||||
void CMainDialog::OnPaperSize()
|
||||
{
|
||||
CPaperSizeDlg dlg(this);
|
||||
dlg.m_widthMm = m_layout.m_paperWidthHu / 100.0;
|
||||
dlg.m_heightMm = m_layout.m_paperHeightHu / 100.0;
|
||||
dlg.m_dpi = m_layout.m_dpi;
|
||||
dlg.m_marginMm = m_layout.m_marginHu / 100.0;
|
||||
if (dlg.DoModal() == IDOK)
|
||||
{
|
||||
m_layout.m_paperWidthHu = (int)(dlg.m_widthMm * 100 + 0.5);
|
||||
m_layout.m_paperHeightHu = (int)(dlg.m_heightMm * 100 + 0.5);
|
||||
m_layout.m_dpi = dlg.m_dpi;
|
||||
m_layout.m_marginHu = (int)(dlg.m_marginMm * 100 + 0.5);
|
||||
m_layout.SyncTemplateRect();
|
||||
m_canvas.SetDirty(TRUE);
|
||||
m_canvas.FitZoom();
|
||||
m_canvas.Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 印刷(GDI 直接印刷) =====
|
||||
|
||||
// 「印刷」。プリンタ選択ダイアログ→プリンタ DC を取得し、画面と同じ
|
||||
// Render::DrawLayout で 1 ページ印刷する(StartDoc/StartPage/EndPage/EndDoc)。
|
||||
void CMainDialog::OnFilePrint()
|
||||
{
|
||||
CPrintDialog pd(FALSE,
|
||||
PD_RETURNDC | PD_USEDEVMODECOPIES | PD_NOPAGENUMS | PD_NOSELECTION | PD_ALLPAGES,
|
||||
this);
|
||||
if (pd.DoModal() != IDOK)
|
||||
return;
|
||||
|
||||
HDC hdc = pd.GetPrinterDC();
|
||||
if (!hdc)
|
||||
{
|
||||
AfxMessageBox(_T("プリンタ DC を取得できませんでした。"));
|
||||
return;
|
||||
}
|
||||
|
||||
CDC dc;
|
||||
dc.Attach(hdc);
|
||||
|
||||
DOCINFO di;
|
||||
ZeroMemory(&di, sizeof(di));
|
||||
di.cbSize = sizeof(di);
|
||||
di.lpszDocName = _T("AveryDennisonUtil");
|
||||
|
||||
if (dc.StartDoc(&di) > 0)
|
||||
{
|
||||
if (dc.StartPage() > 0)
|
||||
{
|
||||
int dpiX = dc.GetDeviceCaps(LOGPIXELSX);
|
||||
int dpiY = dc.GetDeviceCaps(LOGPIXELSY);
|
||||
// プリンタ DC の原点(0,0)は「印字可能領域」の左上。ラベル mm(0,0) を
|
||||
// そこに合わせる(印字可能領域の考慮)。物理マージン分を引くと逆に
|
||||
// 非印字領域へはみ出して切れるため、オフセットは加えない。
|
||||
CoordMap map(dpiX, dpiY, 1.0, 0, 0); // 実 DPI・原点(0,0)・等倍
|
||||
Render::DrawLayout(dc, m_layout, map, /*screen=*/false, nullptr);
|
||||
dc.EndPage();
|
||||
}
|
||||
dc.EndDoc();
|
||||
}
|
||||
|
||||
dc.Detach();
|
||||
::DeleteDC(hdc);
|
||||
}
|
||||
|
||||
// 「バージョン情報」ダイアログを表示する。
|
||||
void CMainDialog::OnAppAbout()
|
||||
{
|
||||
CAboutDlg dlg;
|
||||
dlg.DoModal();
|
||||
}
|
||||
|
||||
// ===== 編集操作(キャンバスへ委譲) =====
|
||||
|
||||
void CMainDialog::OnItemCopy() { m_canvas.CopySelected(); }
|
||||
void CMainDialog::OnItemPaste() { m_canvas.Paste(); }
|
||||
void CMainDialog::OnItemDuplicate() { m_canvas.DuplicateSelected(); }
|
||||
void CMainDialog::OnItemFront() { m_canvas.BringSelectedToFront(); }
|
||||
void CMainDialog::OnItemBack() { m_canvas.SendSelectedToBack(); }
|
||||
|
||||
// 「整列...」ボタン。左/中央/右・上/中/下のポップアップメニューを出す。
|
||||
// 選択された項目は ON_COMMAND_RANGE 経由で OnAlignCmd に届く。
|
||||
void CMainDialog::OnAlignMenu()
|
||||
{
|
||||
if (!m_canvas.HasSelection())
|
||||
{
|
||||
AfxMessageBox(_T("整列するアイテムを選択してください。"));
|
||||
return;
|
||||
}
|
||||
CMenu menu;
|
||||
menu.CreatePopupMenu();
|
||||
menu.AppendMenu(MF_STRING, ID_ALIGN_LEFT, _T("左揃え"));
|
||||
menu.AppendMenu(MF_STRING, ID_ALIGN_HCENTER, _T("左右中央"));
|
||||
menu.AppendMenu(MF_STRING, ID_ALIGN_RIGHT, _T("右揃え"));
|
||||
menu.AppendMenu(MF_SEPARATOR, 0, (LPCTSTR)nullptr);
|
||||
menu.AppendMenu(MF_STRING, ID_ALIGN_TOP, _T("上揃え"));
|
||||
menu.AppendMenu(MF_STRING, ID_ALIGN_VCENTER, _T("上下中央"));
|
||||
menu.AppendMenu(MF_STRING, ID_ALIGN_BOTTOM, _T("下揃え"));
|
||||
|
||||
CRect rc;
|
||||
GetDlgItem(ID_ALIGN_MENU)->GetWindowRect(&rc);
|
||||
menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_TOPALIGN | TPM_LEFTBUTTON,
|
||||
rc.right, rc.top, this);
|
||||
}
|
||||
|
||||
// 整列メニュー項目のハンドラ。ID の連番を how(0..5)に変換して整列を実行。
|
||||
void CMainDialog::OnAlignCmd(UINT nID)
|
||||
{
|
||||
m_canvas.AlignSelected((int)(nID - ID_ALIGN_LEFT));
|
||||
}
|
||||
|
||||
// 「グリッド/スナップ」チェックの切替をキャンバスへ反映する。
|
||||
void CMainDialog::OnToggleGrid()
|
||||
{
|
||||
CButton* p = (CButton*)GetDlgItem(IDC_CHECK_GRID);
|
||||
if (p) m_canvas.SetSnap(p->GetCheck() == BST_CHECKED);
|
||||
}
|
||||
|
||||
// ===== 原紙(背景下絵) =====
|
||||
|
||||
// 「原紙を印刷」チェックの表示をモデル(m_printTemplate)に合わせる。
|
||||
void CMainDialog::UpdateTemplateCheck()
|
||||
{
|
||||
CButton* p = (CButton*)GetDlgItem(IDC_CHECK_PRINT_TEMPLATE);
|
||||
if (p) p->SetCheck(m_layout.m_printTemplate ? BST_CHECKED : BST_UNCHECKED);
|
||||
}
|
||||
|
||||
// 「原紙...」ボタン。画像を読み込み、続いて用紙サイズを原紙に合わせるか
|
||||
// (埋め込みDPI / 用紙DPI / 変更しない)をダイアログで選ばせる。
|
||||
void CMainDialog::OnTemplateLoad()
|
||||
{
|
||||
CFileDialog dlg(TRUE, nullptr, nullptr,
|
||||
OFN_FILEMUSTEXIST | OFN_HIDEREADONLY,
|
||||
_T("画像ファイル (*.png;*.jpg;*.jpeg;*.bmp;*.gif)|*.png;*.jpg;*.jpeg;*.bmp;*.gif|すべて (*.*)|*.*||"),
|
||||
this);
|
||||
if (dlg.DoModal() != IDOK) return;
|
||||
if (!m_layout.LoadTemplate(dlg.GetPathName()))
|
||||
{
|
||||
AfxMessageBox(_T("原紙を読み込めませんでした。"));
|
||||
return;
|
||||
}
|
||||
|
||||
// 用紙サイズを原紙ファイルのサイズに合わせる(px→mm 換算の基準を選択)
|
||||
int pw = 0, ph = 0;
|
||||
if (m_layout.GetTemplatePixelSize(pw, ph) && pw > 0 && ph > 0)
|
||||
{
|
||||
int pdpi = m_layout.m_dpi;
|
||||
double papW = pw * 25.4 / pdpi, papH = ph * 25.4 / pdpi;
|
||||
|
||||
double edx = 0, edy = 0;
|
||||
BOOL hasEmbed = m_layout.GetTemplateDpi(edx, edy);
|
||||
|
||||
CString msg;
|
||||
int r;
|
||||
if (hasEmbed)
|
||||
{
|
||||
double embW = pw * 25.4 / edx, embH = ph * 25.4 / edy;
|
||||
msg.Format(
|
||||
_T("用紙サイズを原紙に合わせます。\n\n画素: %d × %d px\n\n")
|
||||
_T("[はい] 画像の埋め込みDPI %.0f dpi → %.1f × %.1f mm\n")
|
||||
_T("[いいえ] 用紙のDPI %d dpi → %.1f × %.1f mm\n")
|
||||
_T("[キャンセル] 用紙サイズは変更しない"),
|
||||
pw, ph, edx, embW, embH, pdpi, papW, papH);
|
||||
r = AfxMessageBox(msg, MB_YESNOCANCEL | MB_ICONQUESTION);
|
||||
if (r == IDYES) m_layout.SetPaperSizeFromTemplate(TRUE);
|
||||
else if (r == IDNO) m_layout.SetPaperSizeFromTemplate(FALSE);
|
||||
// IDCANCEL: 用紙サイズは変更しない(原紙は現用紙に伸縮表示)
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.Format(
|
||||
_T("用紙サイズを原紙に合わせます。\n(画像に埋め込みDPI情報がありません)\n\n")
|
||||
_T("画素: %d × %d px\n\n")
|
||||
_T("[はい] 用紙のDPI %d dpi → %.1f × %.1f mm で合わせる\n")
|
||||
_T("[いいえ] 用紙サイズは変更しない"),
|
||||
pw, ph, pdpi, papW, papH);
|
||||
r = AfxMessageBox(msg, MB_YESNO | MB_ICONQUESTION);
|
||||
if (r == IDYES) m_layout.SetPaperSizeFromTemplate(FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
m_canvas.SetDirty(TRUE);
|
||||
m_canvas.FitZoom();
|
||||
m_canvas.Invalidate();
|
||||
}
|
||||
|
||||
// 「消去」ボタン。読み込んだ原紙を破棄して再描画する。
|
||||
void CMainDialog::OnTemplateClear()
|
||||
{
|
||||
if (!m_layout.HasTemplate()) return;
|
||||
m_layout.ClearTemplate();
|
||||
m_canvas.SetDirty(TRUE);
|
||||
m_canvas.Invalidate();
|
||||
}
|
||||
|
||||
// 「原紙を印刷」チェックの切替をモデル(m_printTemplate)へ反映する。
|
||||
void CMainDialog::OnTogglePrintTemplate()
|
||||
{
|
||||
CButton* p = (CButton*)GetDlgItem(IDC_CHECK_PRINT_TEMPLATE);
|
||||
if (p)
|
||||
{
|
||||
m_layout.m_printTemplate = (p->GetCheck() == BST_CHECKED);
|
||||
m_canvas.SetDirty(TRUE);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user