- 用紙設計キャンバス(1/100mm 基準)・テキスト/画像/矩形/直線アイテム - コピー/複製/Zオーダー/整列/グリッドスナップ、原紙(下絵)読込と印刷可否 - GDI 直接印刷、.otz 保存/再読込(版数つき) - MFC/CRT 静的リンクで VS2022 IDE から F5 デバッグ可
584 lines
18 KiB
C++
584 lines
18 KiB
C++
// Canvas.cpp
|
|
#include "pch.h"
|
|
#include "Canvas.h"
|
|
#include "LabelLayout.h"
|
|
#include "RenderScene.h"
|
|
#include "TextItem.h"
|
|
#include "ShapeItem.h"
|
|
#include "ImageItem.h"
|
|
#include "TextPropsDlg.h"
|
|
|
|
BEGIN_MESSAGE_MAP(CCanvas, CWnd)
|
|
ON_WM_PAINT()
|
|
ON_WM_ERASEBKGND()
|
|
ON_WM_LBUTTONDOWN()
|
|
ON_WM_LBUTTONUP()
|
|
ON_WM_MOUSEMOVE()
|
|
ON_WM_LBUTTONDBLCLK()
|
|
ON_WM_SETCURSOR()
|
|
ON_WM_KEYDOWN()
|
|
ON_WM_GETDLGCODE()
|
|
END_MESSAGE_MAP()
|
|
|
|
// コンストラクタ。ズーム3倍・スナップ有効・グリッド1mm を既定値に初期化。
|
|
CCanvas::CCanvas()
|
|
: m_zoom(3.0)
|
|
, m_pLayout(nullptr)
|
|
, m_pSelected(nullptr)
|
|
, m_pClipboard(nullptr)
|
|
, m_dirty(FALSE)
|
|
, m_snap(TRUE)
|
|
, m_gridHu(100) // 1mm
|
|
, m_drag(Drag::None)
|
|
, m_dragHandle(-1)
|
|
, m_dragStartHu(0, 0)
|
|
, m_origRect(0, 0, 0, 0)
|
|
{
|
|
}
|
|
|
|
// デストラクタ。コピー用クリップボードのアイテムを解放する。
|
|
CCanvas::~CCanvas()
|
|
{
|
|
delete m_pClipboard;
|
|
}
|
|
|
|
// 値(1/100mm)を最も近いグリッド線へ丸める。スナップ移動・リサイズで使用。
|
|
int CCanvas::SnapHu(int hu) const
|
|
{
|
|
if (m_gridHu <= 0) return hu;
|
|
int half = m_gridHu / 2;
|
|
return ((hu + (hu >= 0 ? half : -half)) / m_gridHu) * m_gridHu;
|
|
}
|
|
|
|
// 編集キャンバス(子ウィンドウ)を生成する。ダブルクリック受信・背景灰色。
|
|
BOOL CCanvas::Create(CWnd* pParent, const CRect& rc, UINT id)
|
|
{
|
|
LPCTSTR cls = AfxRegisterWndClass(
|
|
CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW,
|
|
::LoadCursor(nullptr, IDC_ARROW),
|
|
(HBRUSH)::GetStockObject(LTGRAY_BRUSH));
|
|
return CWnd::Create(cls, _T(""),
|
|
WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP,
|
|
rc, pParent, id);
|
|
}
|
|
|
|
// 指定 DPI と現在のズーム・余白から座標変換器(CoordMap)を作る。
|
|
CoordMap CCanvas::MakeMap(int dpiX, int dpiY) const
|
|
{
|
|
return CoordMap(dpiX, dpiY, m_zoom, kMargin, kMargin);
|
|
}
|
|
|
|
// 画面 DC の実 DPI を使って座標変換器を作る(上のオーバーロードの簡易版)。
|
|
CoordMap CCanvas::MakeMap() const
|
|
{
|
|
CClientDC dc(const_cast<CCanvas*>(this));
|
|
return MakeMap(dc.GetDeviceCaps(LOGPIXELSX), dc.GetDeviceCaps(LOGPIXELSY));
|
|
}
|
|
|
|
// クライアント座標(ピクセル)を内容座標(1/100mm)へ変換する。
|
|
CPoint CCanvas::HuFromClient(CPoint pt)
|
|
{
|
|
return MakeMap().DevToMm(pt); // スクロール無し: クライアント=内容座標
|
|
}
|
|
|
|
// ---- 描画 ----------------------------------------------------------------
|
|
|
|
BOOL CCanvas::OnEraseBkgnd(CDC* /*pDC*/)
|
|
{
|
|
return TRUE; // OnPaint で全面描画(ちらつき防止)
|
|
}
|
|
|
|
// 再描画。ちらつき防止のためメモリ DC(ダブルバッファ)に一旦描いてから転送する。
|
|
// 用紙外を灰色で塗り、Render::DrawLayout で用紙・原紙・グリッド・アイテムを描く。
|
|
void CCanvas::OnPaint()
|
|
{
|
|
CPaintDC dc(this);
|
|
CRect client;
|
|
GetClientRect(client);
|
|
|
|
// ダブルバッファ
|
|
CDC mem;
|
|
mem.CreateCompatibleDC(&dc);
|
|
CBitmap bmp;
|
|
bmp.CreateCompatibleBitmap(&dc, client.Width(), client.Height());
|
|
CBitmap* pOldBmp = mem.SelectObject(&bmp);
|
|
|
|
// 用紙外(灰色)
|
|
mem.FillSolidRect(client, RGB(200, 200, 200));
|
|
|
|
if (m_pLayout)
|
|
{
|
|
CoordMap map = MakeMap(mem.GetDeviceCaps(LOGPIXELSX), mem.GetDeviceCaps(LOGPIXELSY));
|
|
Render::DrawLayout(mem, *m_pLayout, map, /*screen=*/true, m_pSelected,
|
|
m_snap ? m_gridHu : 0);
|
|
}
|
|
|
|
dc.BitBlt(0, 0, client.Width(), client.Height(), &mem, 0, 0, SRCCOPY);
|
|
mem.SelectObject(pOldBmp);
|
|
}
|
|
|
|
// ---- ズーム --------------------------------------------------------------
|
|
|
|
// 表示倍率を設定する(0.25〜30倍にクランプ)。再描画も行う。
|
|
void CCanvas::SetZoom(double z)
|
|
{
|
|
if (z < 0.25) z = 0.25;
|
|
if (z > 30.0) z = 30.0;
|
|
m_zoom = z;
|
|
Invalidate();
|
|
}
|
|
|
|
// 用紙全体がキャンバスに収まる倍率へ自動調整する。
|
|
void CCanvas::FitZoom()
|
|
{
|
|
if (!m_pLayout) return;
|
|
CRect client;
|
|
GetClientRect(client);
|
|
CClientDC dc(this);
|
|
int dpiX = dc.GetDeviceCaps(LOGPIXELSX);
|
|
int dpiY = dc.GetDeviceCaps(LOGPIXELSY);
|
|
// zoom=1 のときの用紙デバイス幅/高さ
|
|
double pw = (double)m_pLayout->m_paperWidthHu * dpiX / 2540.0;
|
|
double ph = (double)m_pLayout->m_paperHeightHu * dpiY / 2540.0;
|
|
double zx = (client.Width() - kMargin * 2) / max(1.0, pw);
|
|
double zy = (client.Height() - kMargin * 2) / max(1.0, ph);
|
|
SetZoom(min(zx, zy));
|
|
}
|
|
|
|
// ---- マウス --------------------------------------------------------------
|
|
|
|
// 左ボタン押下。選択アイテムのハンドル上ならリサイズ開始、
|
|
// アイテム本体上なら選択+移動開始、何も無ければ選択解除。
|
|
void CCanvas::OnLButtonDown(UINT nFlags, CPoint point)
|
|
{
|
|
SetFocus();
|
|
if (!m_pLayout) return;
|
|
CPoint huPt = HuFromClient(point);
|
|
|
|
if (m_pSelected)
|
|
{
|
|
CoordMap map = MakeMap();
|
|
CRect devRect = map.MmToDev(m_pSelected->Rect());
|
|
int handle = CPrintItem::HitHandle(devRect, point);
|
|
if (handle >= 0)
|
|
{
|
|
m_drag = Drag::Resize;
|
|
m_dragHandle = handle;
|
|
m_dragStartHu = huPt;
|
|
m_origRect = m_pSelected->Rect();
|
|
SetCapture();
|
|
return;
|
|
}
|
|
}
|
|
|
|
CPrintItem* hit = m_pLayout->HitTest(huPt);
|
|
if (hit != m_pSelected)
|
|
{
|
|
m_pSelected = hit;
|
|
Invalidate();
|
|
}
|
|
if (hit)
|
|
{
|
|
m_drag = Drag::Move;
|
|
m_dragStartHu = huPt;
|
|
m_origRect = hit->Rect();
|
|
SetCapture();
|
|
}
|
|
else
|
|
{
|
|
m_drag = Drag::None;
|
|
}
|
|
CWnd::OnLButtonDown(nFlags, point);
|
|
}
|
|
|
|
// マウス移動。ドラッグ中なら選択アイテムを移動/リサイズする。
|
|
// スナップ有効時は結果の座標をグリッドへ吸着。最小サイズも保証。
|
|
void CCanvas::OnMouseMove(UINT nFlags, CPoint point)
|
|
{
|
|
if (m_drag != Drag::None && m_pSelected)
|
|
{
|
|
CPoint huPt = HuFromClient(point);
|
|
int dx = huPt.x - m_dragStartHu.x;
|
|
int dy = huPt.y - m_dragStartHu.y;
|
|
|
|
CRect r = m_origRect;
|
|
if (m_drag == Drag::Move)
|
|
{
|
|
r.OffsetRect(dx, dy);
|
|
if (m_snap)
|
|
{
|
|
// 左上をグリッドへスナップ(サイズは保つ)
|
|
r.OffsetRect(SnapHu(r.left) - r.left, SnapHu(r.top) - r.top);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (m_dragHandle)
|
|
{
|
|
case 0: r.left += dx; r.top += dy; break;
|
|
case 1: r.top += dy; break;
|
|
case 2: r.right += dx; r.top += dy; break;
|
|
case 3: r.right += dx; break;
|
|
case 4: r.right += dx; r.bottom += dy; break;
|
|
case 5: r.bottom += dy; break;
|
|
case 6: r.left += dx; r.bottom += dy; break;
|
|
case 7: r.left += dx; break;
|
|
}
|
|
if (m_snap)
|
|
{
|
|
r.left = SnapHu(r.left); r.top = SnapHu(r.top);
|
|
r.right = SnapHu(r.right); r.bottom = SnapHu(r.bottom);
|
|
}
|
|
if (r.Width() < 50) r.right = r.left + 50;
|
|
if (r.Height() < 50) r.bottom = r.top + 50;
|
|
}
|
|
m_pSelected->SetRect(r);
|
|
m_dirty = TRUE;
|
|
Invalidate();
|
|
}
|
|
CWnd::OnMouseMove(nFlags, point);
|
|
}
|
|
|
|
// 左ボタン離し。ドラッグ(移動/リサイズ)を終了しキャプチャを解放する。
|
|
void CCanvas::OnLButtonUp(UINT nFlags, CPoint point)
|
|
{
|
|
if (m_drag != Drag::None)
|
|
{
|
|
m_drag = Drag::None;
|
|
m_dragHandle = -1;
|
|
if (GetCapture() == this)
|
|
ReleaseCapture();
|
|
}
|
|
CWnd::OnLButtonUp(nFlags, point);
|
|
}
|
|
|
|
// ダブルクリック。アイテム上ならそれを選択してプロパティ編集を開く。
|
|
void CCanvas::OnLButtonDblClk(UINT nFlags, CPoint point)
|
|
{
|
|
if (m_pLayout)
|
|
{
|
|
CPoint huPt = HuFromClient(point);
|
|
CPrintItem* hit = m_pLayout->HitTest(huPt);
|
|
if (hit)
|
|
{
|
|
m_pSelected = hit;
|
|
EditSelectedProperties();
|
|
Invalidate();
|
|
return;
|
|
}
|
|
}
|
|
CWnd::OnLButtonDblClk(nFlags, point);
|
|
}
|
|
|
|
// マウスカーソル形状の制御。選択アイテムのハンドル上では方向に応じた
|
|
// リサイズカーソル(↔ ↕ ⤡ ⤢)に切り替える。
|
|
BOOL CCanvas::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
|
|
{
|
|
if (nHitTest == HTCLIENT && m_pSelected)
|
|
{
|
|
CPoint pt;
|
|
GetCursorPos(&pt);
|
|
ScreenToClient(&pt);
|
|
CoordMap map = MakeMap();
|
|
CRect devRect = map.MmToDev(m_pSelected->Rect());
|
|
int h = CPrintItem::HitHandle(devRect, pt);
|
|
if (h >= 0)
|
|
{
|
|
LPCTSTR cur = IDC_SIZEALL;
|
|
switch (h)
|
|
{
|
|
case 0: case 4: cur = IDC_SIZENWSE; break;
|
|
case 2: case 6: cur = IDC_SIZENESW; break;
|
|
case 1: case 5: cur = IDC_SIZENS; break;
|
|
case 3: case 7: cur = IDC_SIZEWE; break;
|
|
}
|
|
::SetCursor(::LoadCursor(nullptr, cur));
|
|
return TRUE;
|
|
}
|
|
}
|
|
return CWnd::OnSetCursor(pWnd, nHitTest, message);
|
|
}
|
|
|
|
// キー操作。Delete=削除、Ctrl+C/V/D=コピー/貼付/複製、Ctrl+]/[ =前面/背面、
|
|
// 矢印=微調整(スナップ時はグリッド幅・通常0.1mm、Shiftで10倍)。
|
|
void CCanvas::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
|
|
{
|
|
BOOL ctrl = (::GetKeyState(VK_CONTROL) & 0x8000) != 0;
|
|
BOOL shift = (::GetKeyState(VK_SHIFT) & 0x8000) != 0;
|
|
|
|
switch (nChar)
|
|
{
|
|
case VK_DELETE:
|
|
DeleteSelected();
|
|
return;
|
|
case 'C': if (ctrl) { CopySelected(); return; } break;
|
|
case 'V': if (ctrl) { Paste(); return; } break;
|
|
case 'D': if (ctrl) { DuplicateSelected(); return; } break;
|
|
case VK_OEM_6: if (ctrl) { BringSelectedForward(); return; } break; // ']'
|
|
case VK_OEM_4: if (ctrl) { SendSelectedBackward(); return; } break; // '['
|
|
case VK_LEFT: case VK_RIGHT: case VK_UP: case VK_DOWN:
|
|
if (m_pSelected)
|
|
{
|
|
int step = m_snap ? m_gridHu : 10; // 10hu = 0.1mm
|
|
if (shift) step *= 10;
|
|
int dx = 0, dy = 0;
|
|
if (nChar == VK_LEFT) dx = -step;
|
|
else if (nChar == VK_RIGHT) dx = step;
|
|
else if (nChar == VK_UP) dy = -step;
|
|
else dy = step;
|
|
CRect r = m_pSelected->Rect();
|
|
r.OffsetRect(dx, dy);
|
|
m_pSelected->SetRect(r);
|
|
m_dirty = TRUE;
|
|
Invalidate();
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
|
|
}
|
|
|
|
// このコントロールが欲しいキー種別を返す。矢印キーがダイアログのフォーカス移動に
|
|
// 奪われないよう DLGC_WANTARROWS を返し、矢印での微調整を確実に受け取る。
|
|
UINT CCanvas::OnGetDlgCode()
|
|
{
|
|
return DLGC_WANTARROWS | DLGC_WANTCHARS;
|
|
}
|
|
|
|
// ---- 挿入・編集 ----------------------------------------------------------
|
|
|
|
// アイテムをレイアウトへ追加し、それを選択状態にして再描画する(挿入系の共通処理)。
|
|
void CCanvas::AddAndSelect(CPrintItem* p)
|
|
{
|
|
m_pLayout->AddItem(p);
|
|
m_pSelected = p;
|
|
m_dirty = TRUE;
|
|
Invalidate();
|
|
}
|
|
|
|
// テキストアイテムを追加。用紙中央に置き、プロパティ入力ダイアログで
|
|
// 内容を確定したら追加する(キャンセル時は破棄)。
|
|
void CCanvas::InsertText()
|
|
{
|
|
if (!m_pLayout) return;
|
|
CTextItem* p = new CTextItem();
|
|
int w = 3000, h = 800;
|
|
int x = (m_pLayout->m_paperWidthHu - w) / 2;
|
|
int y = (m_pLayout->m_paperHeightHu - h) / 2;
|
|
p->SetRect(CRect(x, y, x + w, y + h));
|
|
|
|
CTextPropsDlg dlg(this);
|
|
dlg.m_text = p->m_text;
|
|
dlg.m_fontName = p->m_fontName;
|
|
dlg.m_pointSize = p->m_pointSize;
|
|
dlg.m_align = p->m_align;
|
|
dlg.m_bold = p->m_bold;
|
|
if (dlg.DoModal() == IDOK)
|
|
{
|
|
p->m_text = dlg.m_text;
|
|
p->m_fontName = dlg.m_fontName;
|
|
p->m_pointSize = dlg.m_pointSize;
|
|
p->m_align = dlg.m_align;
|
|
p->m_bold = dlg.m_bold;
|
|
AddAndSelect(p);
|
|
}
|
|
else
|
|
{
|
|
delete p;
|
|
}
|
|
}
|
|
|
|
// 矩形枠アイテムを追加(用紙の約6割×4割サイズで中央配置)。
|
|
void CCanvas::InsertRect()
|
|
{
|
|
if (!m_pLayout) return;
|
|
CShapeItem* p = new CShapeItem();
|
|
p->m_shape = 0;
|
|
int w = m_pLayout->m_paperWidthHu * 6 / 10;
|
|
int h = m_pLayout->m_paperHeightHu * 4 / 10;
|
|
int x = (m_pLayout->m_paperWidthHu - w) / 2;
|
|
int y = (m_pLayout->m_paperHeightHu - h) / 2;
|
|
p->SetRect(CRect(x, y, x + w, y + h));
|
|
AddAndSelect(p);
|
|
}
|
|
|
|
// 直線アイテムを追加(用紙中央の水平線)。
|
|
void CCanvas::InsertLine()
|
|
{
|
|
if (!m_pLayout) return;
|
|
CShapeItem* p = new CShapeItem();
|
|
p->m_shape = 1;
|
|
int y = m_pLayout->m_paperHeightHu / 2;
|
|
p->SetRect(CRect(m_pLayout->m_paperWidthHu / 10, y,
|
|
m_pLayout->m_paperWidthHu * 9 / 10, y));
|
|
AddAndSelect(p);
|
|
}
|
|
|
|
// 画像アイテムを追加。ファイル選択→読み込み成功なら用紙中央に半分サイズで配置。
|
|
void CCanvas::InsertImage()
|
|
{
|
|
if (!m_pLayout) return;
|
|
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;
|
|
|
|
CImageItem* p = new CImageItem();
|
|
if (!p->LoadFromFile(dlg.GetPathName()))
|
|
{
|
|
delete p;
|
|
AfxMessageBox(_T("画像を読み込めませんでした。"));
|
|
return;
|
|
}
|
|
int w = m_pLayout->m_paperWidthHu / 2;
|
|
int h = m_pLayout->m_paperHeightHu / 2;
|
|
int x = (m_pLayout->m_paperWidthHu - w) / 2;
|
|
int y = (m_pLayout->m_paperHeightHu - h) / 2;
|
|
p->SetRect(CRect(x, y, x + w, y + h));
|
|
AddAndSelect(p);
|
|
}
|
|
|
|
// 選択アイテムのプロパティを編集する。現状はテキストのみ編集ダイアログを持つ。
|
|
void CCanvas::EditSelectedProperties()
|
|
{
|
|
if (!m_pSelected) return;
|
|
if (m_pSelected->Type() == ItemType::Text)
|
|
{
|
|
CTextItem* t = static_cast<CTextItem*>(m_pSelected);
|
|
CTextPropsDlg dlg(this);
|
|
dlg.m_text = t->m_text;
|
|
dlg.m_fontName = t->m_fontName;
|
|
dlg.m_pointSize = t->m_pointSize;
|
|
dlg.m_align = t->m_align;
|
|
dlg.m_bold = t->m_bold;
|
|
if (dlg.DoModal() == IDOK)
|
|
{
|
|
t->m_text = dlg.m_text;
|
|
t->m_fontName = dlg.m_fontName;
|
|
t->m_pointSize = dlg.m_pointSize;
|
|
t->m_align = dlg.m_align;
|
|
t->m_bold = dlg.m_bold;
|
|
m_dirty = TRUE;
|
|
Invalidate();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AfxMessageBox(_T("この種類のアイテムには編集可能なプロパティがありません。"));
|
|
}
|
|
}
|
|
|
|
// 選択中アイテムを削除する。
|
|
void CCanvas::DeleteSelected()
|
|
{
|
|
if (!m_pSelected || !m_pLayout) return;
|
|
m_pLayout->DeleteItem(m_pSelected);
|
|
m_pSelected = nullptr;
|
|
m_dirty = TRUE;
|
|
Invalidate();
|
|
}
|
|
|
|
// ---- コピー / 複製 -------------------------------------------------------
|
|
|
|
// 選択アイテムをクリップボード(自前保持)へ複製して控える。
|
|
void CCanvas::CopySelected()
|
|
{
|
|
if (!m_pSelected) return;
|
|
delete m_pClipboard;
|
|
m_pClipboard = m_pSelected->Clone();
|
|
}
|
|
|
|
// クリップボードの内容を 1mm ずらして貼り付け、選択状態にする。
|
|
void CCanvas::Paste()
|
|
{
|
|
if (!m_pClipboard || !m_pLayout) return;
|
|
CPrintItem* p = m_pClipboard->Clone();
|
|
CRect r = p->Rect();
|
|
r.OffsetRect(100, 100); // 1mm ずらして重なりを避ける
|
|
p->SetRect(r);
|
|
AddAndSelect(p);
|
|
}
|
|
|
|
// 選択アイテムをその場で複製し(1mm ずらし)、複製を選択状態にする。
|
|
void CCanvas::DuplicateSelected()
|
|
{
|
|
if (!m_pSelected || !m_pLayout) return;
|
|
CPrintItem* p = m_pSelected->Clone();
|
|
CRect r = p->Rect();
|
|
r.OffsetRect(100, 100);
|
|
p->SetRect(r);
|
|
AddAndSelect(p);
|
|
}
|
|
|
|
// ---- z オーダー ----------------------------------------------------------
|
|
|
|
// 選択アイテムを最前面へ。
|
|
void CCanvas::BringSelectedToFront()
|
|
{
|
|
if (!m_pSelected || !m_pLayout) return;
|
|
m_pLayout->BringToFront(m_pSelected);
|
|
m_dirty = TRUE;
|
|
Invalidate();
|
|
}
|
|
|
|
// 選択アイテムを最背面へ。
|
|
void CCanvas::SendSelectedToBack()
|
|
{
|
|
if (!m_pSelected || !m_pLayout) return;
|
|
m_pLayout->SendToBack(m_pSelected);
|
|
m_dirty = TRUE;
|
|
Invalidate();
|
|
}
|
|
|
|
// 選択アイテムを1つ前面へ。
|
|
void CCanvas::BringSelectedForward()
|
|
{
|
|
if (!m_pSelected || !m_pLayout) return;
|
|
m_pLayout->BringForward(m_pSelected);
|
|
m_dirty = TRUE;
|
|
Invalidate();
|
|
}
|
|
|
|
// 選択アイテムを1つ背面へ。
|
|
void CCanvas::SendSelectedBackward()
|
|
{
|
|
if (!m_pSelected || !m_pLayout) return;
|
|
m_pLayout->SendBackward(m_pSelected);
|
|
m_dirty = TRUE;
|
|
Invalidate();
|
|
}
|
|
|
|
// ---- 整列(用紙/余白の内側矩形を基準) ------------------------------------
|
|
|
|
// 選択アイテムを基準矩形(余白があれば余白内側、無ければ用紙)に対して整列する。
|
|
// how: 0=左 1=水平中央 2=右 3=上 4=垂直中央 5=下。サイズは変えず位置だけ移動。
|
|
void CCanvas::AlignSelected(int how)
|
|
{
|
|
if (!m_pSelected || !m_pLayout) return;
|
|
CRect b = m_pLayout->ContentRect();
|
|
CRect r = m_pSelected->Rect();
|
|
int w = r.Width(), h = r.Height();
|
|
switch (how)
|
|
{
|
|
case 0: r.left = b.left; r.right = r.left + w; break; // 左
|
|
case 1: { int cx = (b.left + b.right) / 2; r.left = cx - w / 2; r.right = r.left + w; } break; // 水平中央
|
|
case 2: r.right = b.right; r.left = r.right - w; break; // 右
|
|
case 3: r.top = b.top; r.bottom = r.top + h; break; // 上
|
|
case 4: { int cy = (b.top + b.bottom) / 2; r.top = cy - h / 2; r.bottom = r.top + h; } break; // 垂直中央
|
|
case 5: r.bottom = b.bottom; r.top = r.bottom - h; break; // 下
|
|
}
|
|
m_pSelected->SetRect(r);
|
|
m_dirty = TRUE;
|
|
Invalidate();
|
|
}
|
|
|
|
// 「新規」「開く」でレイアウトが差し替わった後に呼ぶ。選択解除して再描画する。
|
|
void CCanvas::OnLayoutReplaced()
|
|
{
|
|
m_pSelected = nullptr;
|
|
m_dirty = FALSE;
|
|
Invalidate();
|
|
}
|