Add a context menu, with a grab bag of options. That will need some
refinement later, but it does not affect file formats so it's not very critical. [git-p4: depot-paths = "//depot/solvespace/": change = 2032]
This commit is contained in:
parent
274005c02c
commit
9416faca88
274
draw.cpp
274
draw.cpp
@ -20,6 +20,8 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
|
||||
bool middleDown, bool rightDown, bool shiftDown, bool ctrlDown)
|
||||
{
|
||||
if(GraphicsEditControlIsVisible()) return;
|
||||
if(context.active) return;
|
||||
|
||||
if(rightDown) {
|
||||
middleDown = true;
|
||||
shiftDown = !shiftDown;
|
||||
@ -34,6 +36,13 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
|
||||
|
||||
Point2d mp = { x, y };
|
||||
|
||||
if(rightDown && orig.mouse.DistanceTo(mp) < 5 && !orig.startedMoving) {
|
||||
// Avoid accidentally panning (or rotating if shift is down) if the
|
||||
// user wants a context menu.
|
||||
return;
|
||||
}
|
||||
orig.startedMoving = true;
|
||||
|
||||
// If the middle button is down, then mouse movement is used to pan and
|
||||
// rotate our view. This wins over everything else.
|
||||
if(middleDown) {
|
||||
@ -347,18 +356,174 @@ void GraphicsWindow::ClearPending(void) {
|
||||
void GraphicsWindow::MouseMiddleOrRightDown(double x, double y) {
|
||||
if(GraphicsEditControlIsVisible()) return;
|
||||
|
||||
if(pending.operation == DRAGGING_NEW_LINE_POINT) {
|
||||
// Special case; use a middle or right click to stop drawing lines,
|
||||
// since a left click would draw another one. This is quicker and
|
||||
// more intuitive than hitting escape.
|
||||
ClearPending();
|
||||
}
|
||||
|
||||
orig.offset = offset;
|
||||
orig.projUp = projUp;
|
||||
orig.projRight = projRight;
|
||||
orig.mouse.x = x;
|
||||
orig.mouse.y = y;
|
||||
orig.startedMoving = false;
|
||||
}
|
||||
|
||||
void GraphicsWindow::ContextMenuListStyles(void) {
|
||||
CreateContextSubmenu();
|
||||
Style *s;
|
||||
bool empty = true;
|
||||
for(s = SK.style.First(); s; s = SK.style.NextAfter(s)) {
|
||||
if(s->h.v < Style::FIRST_CUSTOM) continue;
|
||||
|
||||
AddContextMenuItem(s->DescriptionString(), CMNU_FIRST_STYLE + s->h.v);
|
||||
empty = false;
|
||||
}
|
||||
|
||||
if(!empty) AddContextMenuItem(NULL, CONTEXT_SEPARATOR);
|
||||
|
||||
AddContextMenuItem("No Style", CMNU_NO_STYLE);
|
||||
AddContextMenuItem("Newly Created Custom Style...", CMNU_NEW_CUSTOM_STYLE);
|
||||
}
|
||||
|
||||
void GraphicsWindow::MouseRightUp(double x, double y) {
|
||||
// Don't show a context menu if the user is right-clicking the toolbar,
|
||||
// or if they are finishing a pan.
|
||||
if(ToolbarMouseMoved((int)x, (int)y)) return;
|
||||
if(orig.startedMoving) return;
|
||||
|
||||
if(context.active) return;
|
||||
context.active = true;
|
||||
|
||||
if(pending.operation == DRAGGING_NEW_LINE_POINT) {
|
||||
// Special case; use a right click to stop drawing lines, since
|
||||
// a left click would draw another one. This is quicker and more
|
||||
// intuitive than hitting escape.
|
||||
ClearPending();
|
||||
}
|
||||
|
||||
GroupSelection();
|
||||
if(hover.IsEmpty() && gs.n == 0 && gs.constraints == 0) {
|
||||
// No reason to display a context menu.
|
||||
goto done;
|
||||
}
|
||||
|
||||
// We can either work on the selection (like the functions are designed to)
|
||||
// or on the hovered item. In the latter case we can fudge things by just
|
||||
// selecting the hovered item, and then applying our operation to the
|
||||
// selection.
|
||||
bool toggleForStyles = false,
|
||||
toggleForGroupInfo = false,
|
||||
toggleForDelete = false;
|
||||
|
||||
if(!hover.IsEmpty()) {
|
||||
AddContextMenuItem("Toggle Hovered Item Selection",
|
||||
CMNU_TOGGLE_SELECTION);
|
||||
}
|
||||
|
||||
if(gs.entities > 0) {
|
||||
ContextMenuListStyles();
|
||||
AddContextMenuItem("Assign Selection to Style", CONTEXT_SUBMENU);
|
||||
} else if(hover.entity.v && gs.n == 0 && gs.constraints == 0) {
|
||||
ContextMenuListStyles();
|
||||
AddContextMenuItem("Assign Hovered Item to Style", CONTEXT_SUBMENU);
|
||||
toggleForStyles = true;
|
||||
}
|
||||
|
||||
if(gs.n + gs.constraints == 1) {
|
||||
AddContextMenuItem("Group Info for Selected Item", CMNU_GROUP_INFO);
|
||||
} else if(!hover.IsEmpty() && gs.n == 0 && gs.constraints == 0) {
|
||||
AddContextMenuItem("Group Info for Hovered Item", CMNU_GROUP_INFO);
|
||||
toggleForGroupInfo = true;
|
||||
}
|
||||
|
||||
if(hover.constraint.v && gs.n == 0 && gs.constraints == 0) {
|
||||
Constraint *c = SK.GetConstraint(hover.constraint);
|
||||
if(c->HasLabel()) {
|
||||
AddContextMenuItem("Toggle Reference Dimension",
|
||||
CMNU_REFERENCE_DIM);
|
||||
}
|
||||
if(c->type == Constraint::ANGLE ||
|
||||
c->type == Constraint::EQUAL_ANGLE)
|
||||
{
|
||||
AddContextMenuItem("Other Supplementary Angle",
|
||||
CMNU_OTHER_ANGLE);
|
||||
}
|
||||
}
|
||||
|
||||
if(gs.n > 0 || gs.constraints > 0) {
|
||||
AddContextMenuItem(NULL, CONTEXT_SEPARATOR);
|
||||
AddContextMenuItem("Delete Selection", CMNU_DELETE_SEL);
|
||||
AddContextMenuItem("Unselect All", CMNU_UNSELECT_ALL);
|
||||
} else if(!hover.IsEmpty()) {
|
||||
AddContextMenuItem(NULL, CONTEXT_SEPARATOR);
|
||||
AddContextMenuItem("Delete Hovered Item", CMNU_DELETE_SEL);
|
||||
toggleForDelete = true;
|
||||
}
|
||||
|
||||
int ret = ShowContextMenu();
|
||||
switch(ret) {
|
||||
case CMNU_TOGGLE_SELECTION:
|
||||
ToggleSelectionStateOfHovered();
|
||||
break;
|
||||
|
||||
case CMNU_UNSELECT_ALL:
|
||||
MenuEdit(MNU_UNSELECT_ALL);
|
||||
break;
|
||||
|
||||
case CMNU_DELETE_SEL:
|
||||
if(toggleForDelete) ToggleSelectionStateOfHovered();
|
||||
MenuEdit(MNU_DELETE);
|
||||
break;
|
||||
|
||||
case CMNU_REFERENCE_DIM:
|
||||
ToggleSelectionStateOfHovered();
|
||||
Constraint::MenuConstrain(MNU_REFERENCE);
|
||||
break;
|
||||
|
||||
case CMNU_OTHER_ANGLE:
|
||||
ToggleSelectionStateOfHovered();
|
||||
Constraint::MenuConstrain(MNU_OTHER_ANGLE);
|
||||
break;
|
||||
|
||||
case CMNU_GROUP_INFO: {
|
||||
if(toggleForGroupInfo) ToggleSelectionStateOfHovered();
|
||||
GroupSelection();
|
||||
hGroup hg;
|
||||
if(gs.entities == 1) {
|
||||
hg = SK.GetEntity(gs.entity[0])->group;
|
||||
} else if(gs.points == 1) {
|
||||
hg = SK.GetEntity(gs.point[0])->group;
|
||||
} else if(gs.constraints == 1) {
|
||||
hg = SK.GetConstraint(gs.constraint[0])->group;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
ClearSelection();
|
||||
SS.TW.GoToScreen(TextWindow::SCREEN_GROUP_INFO);
|
||||
SS.TW.shown.group = hg;
|
||||
SS.later.showTW = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case CMNU_NEW_CUSTOM_STYLE: {
|
||||
if(toggleForStyles) ToggleSelectionStateOfHovered();
|
||||
DWORD v = Style::CreateCustomStyle();
|
||||
Style::AssignSelectionToStyle(v);
|
||||
break;
|
||||
}
|
||||
|
||||
case CMNU_NO_STYLE:
|
||||
if(toggleForStyles) ToggleSelectionStateOfHovered();
|
||||
Style::AssignSelectionToStyle(0);
|
||||
break;
|
||||
|
||||
default:
|
||||
if(ret >= CMNU_FIRST_STYLE) {
|
||||
if(toggleForStyles) ToggleSelectionStateOfHovered();
|
||||
Style::AssignSelectionToStyle(ret - CMNU_FIRST_STYLE);
|
||||
}
|
||||
// otherwise it was probably cancelled, so do nothing
|
||||
break;
|
||||
}
|
||||
|
||||
done:
|
||||
context.active = false;
|
||||
}
|
||||
|
||||
hRequest GraphicsWindow::AddRequest(int type) {
|
||||
@ -607,43 +772,10 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
|
||||
}
|
||||
|
||||
case 0:
|
||||
default: {
|
||||
default:
|
||||
ClearPending();
|
||||
|
||||
if(hover.IsEmpty()) break;
|
||||
|
||||
// If an item is hovered, then by clicking on it, we toggle its
|
||||
// selection state.
|
||||
int i;
|
||||
for(i = 0; i < MAX_SELECTED; i++) {
|
||||
if(selection[i].Equals(&hover)) {
|
||||
selection[i].Clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(i != MAX_SELECTED) break;
|
||||
|
||||
if(hover.entity.v != 0 && SK.GetEntity(hover.entity)->IsFace()) {
|
||||
// In the interest of speed for the triangle drawing code,
|
||||
// only two faces may be selected at a time.
|
||||
int c = 0;
|
||||
for(i = 0; i < MAX_SELECTED; i++) {
|
||||
hEntity he = selection[i].entity;
|
||||
if(he.v != 0 && SK.GetEntity(he)->IsFace()) {
|
||||
c++;
|
||||
if(c >= 2) selection[i].Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(i = 0; i < MAX_SELECTED; i++) {
|
||||
if(selection[i].IsEmpty()) {
|
||||
selection[i] = hover;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ToggleSelectionStateOfHovered();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SS.later.showTW = true;
|
||||
@ -771,11 +903,14 @@ void GraphicsWindow::MouseScroll(double x, double y, int delta) {
|
||||
}
|
||||
|
||||
void GraphicsWindow::MouseLeave(void) {
|
||||
// Un-hover everything when the mouse leaves our window.
|
||||
hover.Clear();
|
||||
toolbarTooltipped = 0;
|
||||
toolbarHovered = 0;
|
||||
PaintGraphics();
|
||||
// Un-hover everything when the mouse leaves our window, unless there's
|
||||
// currently a context menu shown.
|
||||
if(!context.active) {
|
||||
hover.Clear();
|
||||
toolbarTooltipped = 0;
|
||||
toolbarHovered = 0;
|
||||
PaintGraphics();
|
||||
}
|
||||
}
|
||||
|
||||
bool GraphicsWindow::Selection::Equals(Selection *b) {
|
||||
@ -851,6 +986,51 @@ void GraphicsWindow::ClearNonexistentSelectionItems(void) {
|
||||
if(change) InvalidateGraphics();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Toggle the selection state of the hovered item: if it was selected then
|
||||
// un-select it, and if it wasn't then select it.
|
||||
//-----------------------------------------------------------------------------
|
||||
void GraphicsWindow::ToggleSelectionStateOfHovered(void) {
|
||||
if(hover.IsEmpty()) return;
|
||||
|
||||
// If an item was selected, then we just un-select it.
|
||||
int i;
|
||||
for(i = 0; i < MAX_SELECTED; i++) {
|
||||
if(selection[i].Equals(&hover)) {
|
||||
selection[i].Clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(i != MAX_SELECTED) return;
|
||||
|
||||
// So it's not selected, so we should select it.
|
||||
|
||||
if(hover.entity.v != 0 && SK.GetEntity(hover.entity)->IsFace()) {
|
||||
// In the interest of speed for the triangle drawing code,
|
||||
// only two faces may be selected at a time.
|
||||
int c = 0;
|
||||
for(i = 0; i < MAX_SELECTED; i++) {
|
||||
hEntity he = selection[i].entity;
|
||||
if(he.v != 0 && SK.GetEntity(he)->IsFace()) {
|
||||
c++;
|
||||
if(c >= 2) selection[i].Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(i = 0; i < MAX_SELECTED; i++) {
|
||||
if(selection[i].IsEmpty()) {
|
||||
selection[i] = hover;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Sort the selection according to various critieria: the entities and
|
||||
// constraints separately, counts of certain types of entities (circles,
|
||||
// lines, etc.), and so on.
|
||||
//-----------------------------------------------------------------------------
|
||||
void GraphicsWindow::GroupSelection(void) {
|
||||
memset(&gs, 0, sizeof(gs));
|
||||
int i;
|
||||
|
@ -154,6 +154,8 @@ void GraphicsWindow::Init(void) {
|
||||
showTextWindow = true;
|
||||
ShowTextWindow(showTextWindow);
|
||||
|
||||
context.active = false;
|
||||
|
||||
// Do this last, so that all the menus get updated correctly.
|
||||
EnsureValidActives();
|
||||
}
|
||||
|
18
group.cpp
18
group.cpp
@ -777,21 +777,3 @@ void Group::CopyEntity(IdList<Entity,hEntity> *el,
|
||||
el->Add(&en);
|
||||
}
|
||||
|
||||
void Group::TagEdgesFromLineSegments(SEdgeList *el) {
|
||||
int i, j;
|
||||
for(i = 0; i < SK.entity.n; i++) {
|
||||
Entity *e = &(SK.entity.elem[i]);
|
||||
if(e->group.v != opA.v) continue;
|
||||
if(e->type != Entity::LINE_SEGMENT) continue;
|
||||
|
||||
Vector p0 = SK.GetEntity(e->point[0])->PointGetNum();
|
||||
Vector p1 = SK.GetEntity(e->point[1])->PointGetNum();
|
||||
|
||||
for(j = 0; j < el->l.n; j++) {
|
||||
SEdge *se = &(el->l.elem[j]);
|
||||
if((p0.Equals(se->a) && p1.Equals(se->b))) se->tag = e->h.v;
|
||||
if((p0.Equals(se->b) && p1.Equals(se->a))) se->tag = e->h.v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
4
sketch.h
4
sketch.h
@ -201,7 +201,6 @@ public:
|
||||
hEntity Remap(hEntity in, int copyNumber);
|
||||
void MakeExtrusionLines(EntityList *el, hEntity in);
|
||||
void MakeExtrusionTopBottomFaces(EntityList *el, hEntity pt);
|
||||
void TagEdgesFromLineSegments(SEdgeList *sle);
|
||||
void CopyEntity(EntityList *el,
|
||||
Entity *ep, int timesApplied, int remap,
|
||||
hParam dx, hParam dy, hParam dz,
|
||||
@ -654,6 +653,9 @@ public:
|
||||
static void FreezeDefaultStyles(void);
|
||||
static void LoadFactoryDefaults(void);
|
||||
|
||||
static void AssignSelectionToStyle(DWORD v);
|
||||
static DWORD CreateCustomStyle(void);
|
||||
|
||||
static Style *Get(hStyle hs);
|
||||
static DWORD Color(hStyle hs, bool forExport=false);
|
||||
static float Width(hStyle hs);
|
||||
|
@ -27,7 +27,7 @@ void SolveSpace::CheckLicenseFromRegistry(void) {
|
||||
}
|
||||
|
||||
const int SECONDS_IN_DAY = 60*60*24;
|
||||
license.trialDaysRemaining = 90 -
|
||||
license.trialDaysRemaining = 30 -
|
||||
(int)(((now - license.firstUse))/SECONDS_IN_DAY);
|
||||
}
|
||||
|
||||
|
@ -114,6 +114,12 @@ void ShowTextEditControl(int hr, int c, char *s);
|
||||
void HideTextEditControl(void);
|
||||
BOOL TextEditControlIsVisible(void);
|
||||
|
||||
#define CONTEXT_SUBMENU (-1)
|
||||
#define CONTEXT_SEPARATOR (-2)
|
||||
void AddContextMenuItem(char *legend, int id);
|
||||
void CreateContextSubmenu(void);
|
||||
int ShowContextMenu(void);
|
||||
|
||||
void ShowTextWindow(BOOL visible);
|
||||
void InvalidateText(void);
|
||||
void InvalidateGraphics(void);
|
||||
|
74
style.cpp
74
style.cpp
@ -33,8 +33,8 @@ char *Style::CnfWidth(char *prefix) {
|
||||
char *Style::CnfPrefixToName(char *prefix) {
|
||||
static char name[100];
|
||||
int i = 0, j;
|
||||
strcpy(name, "def-");
|
||||
j = 4;
|
||||
strcpy(name, "#def-");
|
||||
j = 5;
|
||||
while(prefix[i] && j < 90) {
|
||||
if(isupper(prefix[i]) && i != 0) {
|
||||
name[j++] = '-';
|
||||
@ -106,6 +106,46 @@ void Style::FreezeDefaultStyles(void) {
|
||||
}
|
||||
}
|
||||
|
||||
DWORD Style::CreateCustomStyle(void) {
|
||||
SS.UndoRemember();
|
||||
DWORD vs = max(Style::FIRST_CUSTOM, SK.style.MaximumId() + 1);
|
||||
hStyle hs = { vs };
|
||||
(void)Style::Get(hs);
|
||||
return hs.v;
|
||||
}
|
||||
|
||||
void Style::AssignSelectionToStyle(DWORD v) {
|
||||
bool showError = false;
|
||||
SS.GW.GroupSelection();
|
||||
|
||||
SS.UndoRemember();
|
||||
for(int i = 0; i < SS.GW.gs.entities; i++) {
|
||||
hEntity he = SS.GW.gs.entity[i];
|
||||
if(!he.isFromRequest()) {
|
||||
showError = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
hRequest hr = he.request();
|
||||
Request *r = SK.GetRequest(hr);
|
||||
r->style.v = v;
|
||||
SS.later.generateAll = true;
|
||||
}
|
||||
|
||||
if(showError) {
|
||||
Error("Can't assign style to an entity that's derived from another "
|
||||
"entity; try assigning a style to this entity's parent.");
|
||||
}
|
||||
|
||||
SS.GW.ClearSelection();
|
||||
InvalidateGraphics();
|
||||
|
||||
// And show that style's info screen in the text window.
|
||||
SS.TW.GoToScreen(TextWindow::SCREEN_STYLE_INFO);
|
||||
SS.TW.shown.style.v = v;
|
||||
SS.later.showTW = true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Look up a style by its handle. If that style does not exist, then create
|
||||
// the style, according to our table of default styles.
|
||||
@ -241,10 +281,7 @@ void TextWindow::ScreenLoadFactoryDefaultStyles(int link, DWORD v) {
|
||||
}
|
||||
|
||||
void TextWindow::ScreenCreateCustomStyle(int link, DWORD v) {
|
||||
SS.UndoRemember();
|
||||
DWORD vs = max(Style::FIRST_CUSTOM, SK.style.MaximumId() + 1);
|
||||
hStyle hs = { vs };
|
||||
(void)Style::Get(hs);
|
||||
Style::CreateCustomStyle();
|
||||
}
|
||||
|
||||
void TextWindow::ScreenChangeBackgroundColor(int link, DWORD v) {
|
||||
@ -475,29 +512,6 @@ void TextWindow::ShowStyleInfo(void) {
|
||||
}
|
||||
|
||||
void TextWindow::ScreenAssignSelectionToStyle(int link, DWORD v) {
|
||||
bool showError = false;
|
||||
SS.GW.GroupSelection();
|
||||
|
||||
SS.UndoRemember();
|
||||
for(int i = 0; i < SS.GW.gs.entities; i++) {
|
||||
hEntity he = SS.GW.gs.entity[i];
|
||||
if(!he.isFromRequest()) {
|
||||
showError = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
hRequest hr = he.request();
|
||||
Request *r = SK.GetRequest(hr);
|
||||
r->style.v = v;
|
||||
SS.later.generateAll = true;
|
||||
}
|
||||
|
||||
if(showError) {
|
||||
Error("Can't assign style to an entity that's derived from another "
|
||||
"entity; try assigning a style to this entity's parent.");
|
||||
}
|
||||
|
||||
SS.GW.ClearSelection();
|
||||
InvalidateGraphics();
|
||||
Style::AssignSelectionToStyle(v);
|
||||
}
|
||||
|
||||
|
20
ui.h
20
ui.h
@ -313,6 +313,7 @@ public:
|
||||
Vector projRight;
|
||||
Vector projUp;
|
||||
Point2d mouse;
|
||||
bool startedMoving;
|
||||
} orig;
|
||||
|
||||
// When the user is dragging a point, don't solve multiple times without
|
||||
@ -320,6 +321,11 @@ public:
|
||||
// not displayed.
|
||||
bool havePainted;
|
||||
|
||||
// Some state for the context menu.
|
||||
struct {
|
||||
bool active;
|
||||
} context;
|
||||
|
||||
void NormalizeProjectionVectors(void);
|
||||
Point2d ProjectPoint(Vector p);
|
||||
Vector ProjectPoint3(Vector p);
|
||||
@ -422,9 +428,20 @@ public:
|
||||
int n;
|
||||
} gs;
|
||||
void GroupSelection(void);
|
||||
|
||||
void ToggleSelectionStateOfHovered(void);
|
||||
void ClearSuper(void);
|
||||
|
||||
static const int CMNU_TOGGLE_SELECTION = 0x100;
|
||||
static const int CMNU_UNSELECT_ALL = 0x101;
|
||||
static const int CMNU_DELETE_SEL = 0x102;
|
||||
static const int CMNU_NEW_CUSTOM_STYLE = 0x103;
|
||||
static const int CMNU_NO_STYLE = 0x104;
|
||||
static const int CMNU_GROUP_INFO = 0x105;
|
||||
static const int CMNU_REFERENCE_DIM = 0x106;
|
||||
static const int CMNU_OTHER_ANGLE = 0x107;
|
||||
static const int CMNU_FIRST_STYLE = 0x40000000;
|
||||
void ContextMenuListStyles(void);
|
||||
|
||||
// The toolbar, in toolbar.cpp
|
||||
bool ToolbarDrawOrHitTest(int x, int y, bool paint, int *menu);
|
||||
void ToolbarDraw(void);
|
||||
@ -459,6 +476,7 @@ public:
|
||||
void MouseLeftUp(double x, double y);
|
||||
void MouseLeftDoubleClick(double x, double y);
|
||||
void MouseMiddleOrRightDown(double x, double y);
|
||||
void MouseRightUp(double x, double y);
|
||||
void MouseScroll(double x, double y, int delta);
|
||||
void MouseLeave(void);
|
||||
void EditControlDone(char *s);
|
||||
|
@ -49,6 +49,8 @@ char RecentFile[MAX_RECENT][MAX_PATH];
|
||||
HMENU SubMenus[100];
|
||||
HMENU RecentOpenMenu, RecentImportMenu;
|
||||
|
||||
HMENU ContextMenu, ContextSubmenu;
|
||||
|
||||
int ClientIsSmallerBy;
|
||||
|
||||
HFONT FixedFont, LinkFont;
|
||||
@ -94,6 +96,42 @@ void Message(char *str, ...)
|
||||
va_end(f);
|
||||
}
|
||||
|
||||
void AddContextMenuItem(char *label, int id)
|
||||
{
|
||||
if(!ContextMenu) ContextMenu = CreatePopupMenu();
|
||||
|
||||
if(id == CONTEXT_SUBMENU) {
|
||||
AppendMenu(ContextMenu, MF_STRING | MF_POPUP,
|
||||
(UINT_PTR)ContextSubmenu, label);
|
||||
ContextSubmenu = NULL;
|
||||
} else {
|
||||
HMENU m = ContextSubmenu ? ContextSubmenu : ContextMenu;
|
||||
if(id == CONTEXT_SEPARATOR) {
|
||||
AppendMenu(m, MF_SEPARATOR, 0, "");
|
||||
} else {
|
||||
AppendMenu(m, MF_STRING, id, label);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CreateContextSubmenu(void)
|
||||
{
|
||||
ContextSubmenu = CreatePopupMenu();
|
||||
}
|
||||
|
||||
int ShowContextMenu(void)
|
||||
{
|
||||
POINT p;
|
||||
GetCursorPos(&p);
|
||||
int r = TrackPopupMenu(ContextMenu,
|
||||
TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_TOPALIGN,
|
||||
p.x, p.y, 0, GraphicsWnd, NULL);
|
||||
|
||||
DestroyMenu(ContextMenu);
|
||||
ContextMenu = NULL;
|
||||
return r;
|
||||
}
|
||||
|
||||
void CALLBACK TimerCallback(HWND hwnd, UINT msg, UINT_PTR id, DWORD time)
|
||||
{
|
||||
// The timer is periodic, so needs to be killed explicitly.
|
||||
@ -709,6 +747,7 @@ LRESULT CALLBACK GraphicsWndProc(HWND hwnd, UINT msg, WPARAM wParam,
|
||||
case WM_LBUTTONUP:
|
||||
case WM_LBUTTONDBLCLK:
|
||||
case WM_RBUTTONDOWN:
|
||||
case WM_RBUTTONUP:
|
||||
case WM_MBUTTONDOWN: {
|
||||
int x = LOWORD(lParam);
|
||||
int y = HIWORD(lParam);
|
||||
@ -738,6 +777,8 @@ LRESULT CALLBACK GraphicsWndProc(HWND hwnd, UINT msg, WPARAM wParam,
|
||||
SS.GW.MouseLeftDoubleClick(x, y);
|
||||
} else if(msg == WM_MBUTTONDOWN || msg == WM_RBUTTONDOWN) {
|
||||
SS.GW.MouseMiddleOrRightDown(x, y);
|
||||
} else if(msg == WM_RBUTTONUP) {
|
||||
SS.GW.MouseRightUp(x, y);
|
||||
} else if(msg == WM_MOUSEMOVE) {
|
||||
SS.GW.MouseMoved(x, y,
|
||||
!!(wParam & MK_LBUTTON),
|
||||
|
@ -2,10 +2,11 @@
|
||||
grid
|
||||
better text
|
||||
right-click menu
|
||||
faster triangulation
|
||||
interpolating splines
|
||||
wireframe export
|
||||
|
||||
-----
|
||||
faster triangulation
|
||||
interpolating splines
|
||||
copy and paste
|
||||
loop detection
|
||||
IGES export
|
||||
|
Loading…
Reference in New Issue
Block a user