📄 flowcharteditor.cpp
字号:
/* ==========================================================================
CFlowchartEditor
Author : Johan Rosengren, Abstrakt Mekanik AB
Date : 2004-05-01
Purpose : CFlowchartEditor is a flowchart editor, demonstrating
several concepts of CDiagramEditor:
1. Linking objects using a CDiagramEntityContainer-
derived class to manage data
2. Putting CDiagramEditor in a MDI-application, using
a shared CDiagramClipboard-derived clipboard handler.
3. Exporting the diagram to an enhanced metafile that
can be pasted into - for example - Word.
Description : CFlowcharEditor handles several messages and overrides
drawing to manage links. Links are implemented as a
special class, stored in a separate array in
CFlowchartEntityContainer. Object drawing in
CFlowchartEditor also draws the links.
The objects derived from CFlowchartEntity has link
points. Two selected and unlinked objects with free
link points at appropriate position can be linked. If
they are linked, a line is automatically drawn between
the objects, with an arrow head representing the
direction (which can be flipped).
The link can have a label, that is, a text describing
the link.
When objects are moved, other objects linked to the
moved ones might also be moved - depending on the links.
For example, if two objects are linked horizontally and
one is moved up or down, the linked object will also be
moved.
A special type of object is the linkable line. Linkable
lines are lines that can be linked to other objects,
even lines. They can be used to represent more complex
flows, such as several links converging on a single spot.
Lines will also be moved as other objects, but worth to
notice is that they will not be resized, they will keep
their original length and might therefore trigger
movements far from the current one. Linked lines can
only be drawn either vertically or horizontally, that is,
no slant is allowed - this is enforced by the editor
rather than the line class.
Usage : CFlowchartEditor should be instantiated in the same way
as a CDiagramEditor. The two public member functions
CanLink and IsLinked can be used for command enablers
for the commands OnLink, OnUnlink, OnLinkDirection and
OnLinkProperties. The commands link, unlink, reverse
direction of the link and sets/edit the link label
respectively.
The package uses RTTI (run time type information) to
identify object types, using dynamic_cast, so the
project must enable this feature.
========================================================================*/
#include "stdafx.h"
#include "FlowchartEditor.h"
#include "FlowchartEntityContainer.h"
#include "FlowchartMenu.h"
#include "FlowchartLinkPropertiesDialog.h"
#include "FlowchartLinkableLineSegment.h"
#include <math.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CFlowchartEditor
CFlowchartEditor::CFlowchartEditor()
/* ============================================================
Function : CFlowchartEditor::CFlowchartEditor
Description : constructor
Return : void
Parameters : none
Usage :
============================================================*/
{
SetBackgroundColor( RGB( 250, 250, 230 ) );
SetSnapToGrid( TRUE );
SetRestraints( RESTRAINT_VIRTUAL );
SetPopupMenu( new CFlowchartMenu );
}
CFlowchartEditor::~CFlowchartEditor()
/* ============================================================
Function : CFlowchartEditor::~CFlowchartEditor
Description : destructor
Return : void
Parameters : none
Usage :
============================================================*/
{
}
BEGIN_MESSAGE_MAP(CFlowchartEditor, CDiagramEditor)
ON_COMMAND(IDM_FLOWCHARTEDITOR_LINK, OnLink)
ON_COMMAND(IDM_FLOWCHARTEDITOR_UNLINK, OnUnlink)
ON_COMMAND(IDM_FLOWCHARTEDITOR_LINK_DIRECTION, OnLinkDirection)
ON_COMMAND(IDM_FLOWCHARTEDITOR_LINK_PROPERTIES, OnLinkProperties)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CFlowchartEditor overrides
void CFlowchartEditor::DrawGrid( CDC* dc, CRect /*rect*/, double zoom ) const
/* ============================================================
Function : CFlowchartEditor::DrawGrid
Description : Draws the editor grid. We override this to
draw the grid as a series of dots.
Return : void
Parameters : CDC* dc - CDC to draw to
CRect - Complete rect
double zoom - Current zoom level
Usage : Do not call directly.
============================================================*/
{
COLORREF gridcol = GetGridColor();
dc->SelectStockObject( BLACK_PEN );
int stepx = GetVirtualSize().cx / GetGridSize().cx;
int stepy = GetVirtualSize().cy / GetGridSize().cy;
for( int x = 0 ; x <= stepx ; x++ )
for( int y = 0; y <= stepy ; y++ )
dc->SetPixel( round( ( double ) ( GetGridSize().cx * x ) * zoom ), round( ( double ) ( GetGridSize().cy * y ) * zoom ), gridcol );
}
void CFlowchartEditor::DrawObjects( CDC* dc, double zoom ) const
/* ============================================================
Function : CFlowchartEditor::DrawObjects
Description : Draws the object
Return : void
Parameters : CDC* dc - CDC to draw to
double zoom - Zoom level (use this value
instead of GetZoom())
Usage : Overridden to draw links.
============================================================*/
{
CFlowchartEntityContainer* objs = dynamic_cast< CFlowchartEntityContainer * >( GetDiagramEntityContainer() );
if( objs )
{
CFont font;
font.CreateFont( -round( 12.0 * zoom ), 0,0,0,FW_NORMAL,0,0,0,0,0,0,0,0, _T( "Courier New" ) );
int count = GetObjectCount();
int max = objs->GetLinks();
int seg = round( ( double ) GetMarkerSize().cx * zoom / 2 );
POINT pts[ 3 ];
for( int i = 0 ; i < count ; i++ )
{
CDiagramEntity* main = static_cast< CDiagramEntity* >( objs->GetAt( i ) );
CFlowchartEntity* obj = dynamic_cast< CFlowchartEntity* >( objs->GetAt( i ) );
if( obj )
{
for( int t = 0 ; t < max ; t++ )
{
CFlowchartLink* link = objs->GetLinkAt( t );
if( link && link->from == obj->GetName() )
{
dc->SelectStockObject( BLACK_PEN );
dc->SelectStockObject( BLACK_BRUSH );
CFlowchartEntity* to = GetNamedObject( link->to );
BOOL drawArrow = TRUE;
CPoint start;
CPoint end;
start = obj->GetLinkPosition( link->fromtype );
end = to->GetLinkPosition( link->totype );
start.x = round( ( double ) start.x * zoom );
start.y = round( ( double ) start.y * zoom );
end.x = round( ( double ) end.x * zoom );
end.y = round( ( double ) end.y * zoom );
switch( link->fromtype )
{
case LINK_RIGHT:
pts[ 1 ].x = end.x - seg * 2;
pts[ 1 ].y = end.y - seg;
pts[ 2 ].x = end.x - seg * 2;
pts[ 2 ].y = end.y + seg;
break;
case LINK_LEFT:
pts[ 1 ].x = end.x + seg * 2;
pts[ 1 ].y = end.y - seg;
pts[ 2 ].x = end.x + seg * 2;
pts[ 2 ].y = end.y + seg;
break;
case LINK_TOP:
pts[ 1 ].x = end.x - seg;
pts[ 1 ].y = end.y + seg * 2;
pts[ 2 ].x = end.x + seg;
pts[ 2 ].y = end.y + seg * 2;
break;
case LINK_BOTTOM:
pts[ 1 ].x = end.x - seg;
pts[ 1 ].y = end.y - seg * 2;
pts[ 2 ].x = end.x + seg;
pts[ 2 ].y = end.y - seg * 2;
break;
default:
{
switch( link->totype )
{
case LINK_RIGHT:
pts[ 1 ].x = end.x + seg * 2;
pts[ 1 ].y = end.y - seg;
pts[ 2 ].x = end.x + seg * 2;
pts[ 2 ].y = end.y + seg;
break;
case LINK_LEFT:
pts[ 1 ].x = end.x - seg * 2;
pts[ 1 ].y = end.y - seg;
pts[ 2 ].x = end.x - seg * 2;
pts[ 2 ].y = end.y + seg;
break;
case LINK_TOP:
pts[ 1 ].x = end.x - seg;
pts[ 1 ].y = end.y - seg * 2;
pts[ 2 ].x = end.x + seg;
pts[ 2 ].y = end.y - seg * 2;
break;
case LINK_BOTTOM:
pts[ 1 ].x = end.x - seg;
pts[ 1 ].y = end.y + seg * 2;
pts[ 2 ].x = end.x + seg;
pts[ 2 ].y = end.y + seg * 2;
break;
default:
drawArrow = FALSE;
break;
}
}
break;
}
dc->MoveTo( start );
dc->LineTo( end );
pts[ 0 ].x = end.x;
pts[ 0 ].y = end.y;
if( drawArrow )
dc->Polygon( pts, 3 );
CString str = link->title;
if( str.GetLength() )
{
dc->SelectObject( &font );
int mode = dc->SetBkMode( TRANSPARENT );
CRect rect( start, end );
rect.NormalizeRect();
int cy = round( 14.0 * zoom );
int cut = round( ( double ) GetMarkerSize().cx * zoom / 2 );
CRect r( rect.right - cut, rect.top, rect.right - ( rect.Width() + cut ), rect.bottom );
if(rect.top==rect.bottom)
{
CRect r( rect.left, rect.top - ( cy + cut ), rect.right, rect.bottom );
r.NormalizeRect();
dc->DrawText( str, r, DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER | DT_CENTER );
}
else
{
CRect r( rect.right - cut, rect.top, rect.right - ( cy * str.GetLength() + cut ), rect.bottom );
r.NormalizeRect();
dc->DrawText( str, r, DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER | DT_RIGHT );
}
dc->SetBkMode( mode );
dc->SelectStockObject( DEFAULT_GUI_FONT );
}
}
}
}
main->DrawObject( dc, zoom );
}
}
}
void CFlowchartEditor::SaveObjects( CStringArray& stra )
/* ============================================================
Function : CFlowchartEditor::SaveObjects
Description : Saves all the objects to a CStringArray.
Return : void
Parameters : CStringArray& stra - CStringArray to
save to.
Usage : Overridden to save the links as well.
============================================================*/
{
CDiagramEditor::SaveObjects( stra );
CFlowchartEntityContainer* objs = dynamic_cast< CFlowchartEntityContainer * >( GetDiagramEntityContainer() );
if( objs )
{
int max = objs->GetLinks();
for( int t = 0 ; t < max ; t++ )
{
CFlowchartLink* link = objs->GetLinkAt( t );
stra.Add( link->GetString() );
}
}
}
/////////////////////////////////////////////////////////////////////////////
// CFlowchartEditor message handlers
void CFlowchartEditor::OnMouseMove( UINT nFlags, CPoint point )
/* ============================================================
Function : CFlowchartEditor::OnMouseMove
Description : Handler for WM_MOUSEMOVE. We add handling
to close screen redraws as we are moving
lots of stuff (all the attached objects in
addition to the primarily moved). We also
do secondary movements here - that is,
objects linked to the one(s) being moved
must be moved as well, even if they are
not selected.
Return : void
Parameters : UINT nFlags - Not used
CPoint point - Not used
Usage : Called from MFC
============================================================*/
{
if( ( GetInteractMode() == MODE_MOVING ) || ( GetInteractMode() == MODE_RESIZING && GetSelectCount() == 1 ) )
SetRedraw( FALSE );
CDiagramEditor::OnMouseMove( nFlags, point );
if( GetInteractMode() == MODE_RESIZING )
{
if( GetSelectCount() == 1 )
{
CFlowchartLinkableLineSegment* obj = dynamic_cast< CFlowchartLinkableLineSegment* >( GetSelectedObject() );
if( obj )
{
if( obj->GetLeft() != obj->GetRight() && obj->GetTop() != obj->GetBottom() )
{
if( fabs( obj->GetLeft() - obj->GetRight() ) > fabs( obj->GetTop() - obj->GetBottom() ) )
obj->SetBottom( obj->GetTop() );
else
obj->SetRight( obj->GetLeft() );
}
}
}
}
if( GetInteractMode() == MODE_MOVING || GetInteractMode() == MODE_RESIZING)
{
ModifyLinkedPositions();
SetRedraw( TRUE );
RedrawWindow();
}
}
void CFlowchartEditor::OnKeyDown( UINT nChar, UINT nRepCnt, UINT nFlags )
/* ============================================================
Function : CFlowchartEditor::OnKeyDown
Description : Handles the WM_KEYDOWN-message. We
override this as objects might be moved and
we will have to move unlinked objects as
well.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -