1 /****************************************************************************
2 **
3 ** This file is part of the LibreCAD project, a 2D CAD program
4 **
5 ** Copyright (C) 2012 Dongxu Li (dongxuli2011@gmail.com)
6 ** Copyright (C) 2011 R. van Twisk (librecad@rvt.dds.nl)
7 ** Copyright (C) 2001-2003 RibbonSoft. All rights reserved.
8 **
9 **
10 ** This file may be distributed and/or modified under the terms of the
11 ** GNU General Public License version 2 as published by the Free Software
12 ** Foundation and appearing in the file gpl-2.0.txt included in the
13 ** packaging of this file.
14 **
15 ** This program is distributed in the hope that it will be useful,
16 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 ** GNU General Public License for more details.
19 **
20 ** You should have received a copy of the GNU General Public License
21 ** along with this program; if not, write to the Free Software
22 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
23 **
24 ** This copyright notice MUST APPEAR in all copies of the script!
25 **
26 **********************************************************************/
27 
28 #include<vector>
29 #include <QObject>
30 #include <QTextStream>
31 #include "rs_commands.h"
32 
33 #include "rs_system.h"
34 #include "rs_dialogfactory.h"
35 #include "rs_debug.h"
36 
37 namespace {
38 struct LC_CommandItem {
39 	std::vector<std::pair<QString, QString>> const fullCmdList;
40 	std::vector<std::pair<QString, QString>> const shortCmdList;
41 	RS2::ActionType actionType;
42 };
43 
44 // helper function to check and report command collision
45 template<typename T1, typename T2>
isCollisionFree(std::map<T1,T2> const & lookUp,T1 const & key,T2 const & value)46 bool isCollisionFree(std::map<T1, T2> const& lookUp, T1 const& key, T2 const& value)
47 {
48 	if(!lookUp.count(key)) return true;
49 
50 	//report command string collision
51 	QString msg=__FILE__+QObject::tr(": duplicated command: %1 is already taken by %2")
52 			.arg(key).arg(value);
53 
54 	RS_DEBUG->print(RS_Debug::D_ERROR, "%s\n", msg.toStdString().c_str());
55 	return false;
56 }
57 }
58 
59 RS_Commands* RS_Commands::uniqueInstance = nullptr;
60 
61 const char* RS_Commands::FnPrefix = "Fn";
62 const char* RS_Commands::AltPrefix = "Alt-";
63 const char* RS_Commands::MetaPrefix = "Meta-";
64 
65 
instance()66 RS_Commands* RS_Commands::instance() {
67     if (!uniqueInstance) {
68         uniqueInstance = new RS_Commands();
69     }
70     return uniqueInstance;
71 }
72 
73 /**
74  * Constructor. Initiates main command dictionary.
75  * mainCommand keeps a map from translated commands to actionType
76  * shortCommand keeps a list of translated short commands
77  * cmdTranslation contains both ways of mapping between translated and English
78  */
RS_Commands()79 RS_Commands::RS_Commands() {
80     std::initializer_list<LC_CommandItem> commandList={
81         //draw point
82         {
83             //list all <full command, translation> pairs
84             {{"point", QObject::tr("point", "draw point")}},
85 
86             //list all <short command, translation> pairs
87             {{"po", QObject::tr("po", "draw point")}},
88 
89             //action type
90             RS2::ActionDrawPoint
91         },
92         //draw line
93         {
94             {{"line", QObject::tr("line", "draw line")}},
95             {{"li", QObject::tr("li", "draw line")},
96              {"l", QObject::tr("l", "draw line")}},
97             RS2::ActionDrawLine
98         },
99         //draw polyline
100         {
101             {{"polyline", QObject::tr("polyline", "draw polyline")}},
102             {{"pl", QObject::tr("pl", "draw polyline")}},
103             RS2::ActionDrawPolyline
104         },
105         //draw freehand line
106         {
107             {{"free", QObject::tr("free", "draw freehand line")}},
108             {{"fhl", QObject::tr("fhl", "draw freehand line")}},
109             RS2::ActionDrawLineFree
110         },
111         //draw spline
112         {
113             {{"spline", QObject::tr("spline", "draw spline")}},
114             {{"spl", QObject::tr("spl", "draw spline")}},
115             RS2::ActionDrawSpline
116         },
117         //draw spline through points
118         {
119             {{"spline2", QObject::tr("spline2", "spline through points")}},
120             {{"stp", QObject::tr("stp", "spline through points")}},
121             RS2::ActionDrawSplinePoints
122         },
123         //draw parallel line
124         {
125             {{"offset", QObject::tr("offset", "create offset")},
126             {"parallel", QObject::tr("parallel", "create offset")}},
127             {{"o", QObject::tr("o", "create offset")},
128             {"pa", QObject::tr("pa", "create offset")}},
129             RS2::ActionDrawLineParallel
130         },
131         //draw parallel line through point
132         {
133             {{"ptp", QObject::tr("ptp", "parallel through point")}},
134             {{"pp", QObject::tr("pp", "parallel through point")}},
135             RS2::ActionDrawLineParallelThrough
136         },
137         //draw angle bisector
138         {
139             {{"bisect", QObject::tr("bisect", "angle bisector")}},
140             {{"bi", QObject::tr("bi", "angle bisector")}},
141             RS2::ActionDrawLineBisector
142         },
143         //draw line tangent to circle from point
144         {
145             {{"tangentpc", QObject::tr("tangentpc", "tangent point and circle")}},
146             {{"tanpc", QObject::tr("tanpc", "tangent point and circle")}},
147             RS2::ActionDrawLineTangent1
148         },
149         //draw perpendicular line
150         {
151             {{"perp", QObject::tr("perp", "perpendicular line")}},
152             {{"ortho", QObject::tr("ortho", "perpendicular line")}},
153             RS2::ActionDrawLineOrthogonal
154         },
155         //draw vertical line
156         {
157             {{"vertical", QObject::tr("vertical", "vertical line")}},
158             {{"ver", QObject::tr("ver", "vertical line")}},
159             RS2::ActionDrawLineVertical
160         },
161         //draw horizontal line
162         {
163             {{"horizontal", QObject::tr("horizontal", "horizontal line")}},
164             {{"hor", QObject::tr("hor", "horizontal line")}},
165             RS2::ActionDrawLineHorizontal
166         },
167         //draw rectangle
168         {
169             {{"rectangle", QObject::tr("rectangle", "draw rectangle")}},
170             {{"rectang", QObject::tr("rectang", "draw rectangle")},
171              {"rect", QObject::tr("rect", "draw rectangle")},
172             {"rec", QObject::tr("rec", "draw rectangle")}},
173             RS2::ActionDrawLineRectangle
174         },
175         //draw polygon by 2 vertices
176         {
177             {{"polygon2v", QObject::tr("polygon2v", "polygon by 2 vertices")}},
178             {{"poly2", QObject::tr("poly2", "polygon by 2 vertices")}},
179             RS2::ActionDrawLinePolygonCorCor
180         },
181         //draw arc
182         {
183             {{"arc", QObject::tr("arc", "draw arc")}},
184 			{//{"ar", QObject::tr("ar", "draw arc")},
185             {"a", QObject::tr("a", "draw arc")}},
186             RS2::ActionDrawArc3P
187         },
188         //draw circle
189         {
190             {{"circle", QObject::tr("circle", "draw circle")}},
191             {{"ci", QObject::tr("ci", "draw circle")}},
192             RS2::ActionDrawCircle
193         },
194         //draw 2 point circle
195         {
196             {{"circle2", QObject::tr("circle2", "circle 2 points")}},
197             {{"c2", QObject::tr("c2", "circle 2 points")}},
198             RS2::ActionDrawCircle2P
199         },
200         //draw 3 point circle
201         {
202             {{"circle3", QObject::tr("circle3", "circle 3 points")}},
203             {{"c3", QObject::tr("c3", "circle 3 points")}},
204             RS2::ActionDrawCircle3P
205         },
206 	//draw circle with point and radius
207 	{
208 		{{"circlecr", QObject::tr("circlecr", "circle with center and radius")}},
209 		{{"cc", QObject::tr("cc", "circle with center and radius")}},
210 		RS2::ActionDrawCircleCR
211 	},
212         //draw circle tangent to 3 objects
213         {
214             {{"tan3", QObject::tr("tan3", "circle tangent to 3")}},
215             {{"ct3", QObject::tr("ct3", "circle tangent to 3")}},
216             RS2::ActionDrawCircleTan3
217         },
218         //draw inscribed ellipse
219         {
220             {{"ellipseinscribed", QObject::tr("ellipseinscribed", "inscribed ellipse")}},
221             {{"ei", QObject::tr("ei", "inscribed ellipse")},
222             {"ie", QObject::tr("ie", "inscribed ellipse")}},
223             RS2::ActionDrawEllipseInscribe
224         },
225         //draw hatch
226         {
227             {{"hatch", QObject::tr("hatch", "draw hatch")}},
228             {{"ha", QObject::tr("ha", "draw hatch")}},
229             RS2::ActionDrawHatchNoSelect
230         },
231         //draw mtext
232         {
233             {{"mtext", QObject::tr("mtext", "draw mtext")}},
234             {{"mtxt", QObject::tr("mtxt", "draw mtext")}},
235             RS2::ActionDrawMText
236         },
237         //draw text
238         {
239             {{"text", QObject::tr("text", "draw text")}},
240             {{"txt", QObject::tr("txt", "draw text")}},
241             RS2::ActionDrawText
242         },
243         //zoom redraw
244         {
245             {{"regen", QObject::tr("regen", "zoom - redraw")},
246              {"redraw", QObject::tr("redraw", "zoom - redraw")}},
247             {{"rg", QObject::tr("rg", "zoom - redraw")},
248             {"zr", QObject::tr("zr", "zoom - redraw")}},
249             RS2::ActionZoomRedraw
250         },
251         //zoom window
252         {
253             {{"zoomwindow", QObject::tr("zoomwindow", "zoom - window")}},
254             {{"zw", QObject::tr("zw", "zoom - window")}},
255             RS2::ActionZoomWindow
256         },
257         //zoom auto
258         {
259             {{"zoomauto", QObject::tr("zoomauto", "zoom - auto")}},
260             {{"za", QObject::tr("za", "zoom - auto")}},
261             RS2::ActionZoomAuto
262         },
263         //zoom pan
264         {
265             {{"zoompan", QObject::tr("zoompan", "zoom - pan")}},
266             {{"zp", QObject::tr("zp", "zoom - pan")}},
267             RS2::ActionZoomPan
268         },
269         //zoom previous
270         {
271             {{"zoomprevious", QObject::tr("zoomprevious", "zoom - previous")}},
272             {{"zv", QObject::tr("zv", "zoom - previous")}},
273             RS2::ActionZoomPrevious
274         },
275         //kill actions
276         {
277             {{"kill", QObject::tr("kill", "kill all actions")}},
278             {{"k", QObject::tr("k", "kill all actions")}},
279             RS2::ActionEditKillAllActions
280         },
281         //undo cycle
282         {
283             {{"undo", QObject::tr("undo", "undo cycle")}},
284             {{"u", QObject::tr("u", "undo cycle")}},
285             RS2::ActionEditUndo
286         },
287         //redo cycle
288         {
289             {{"redo", QObject::tr("redo", "redo cycle")}},
290             {{"r", QObject::tr("r", "redo cycle")}},
291             RS2::ActionEditRedo
292         },
293         //dimension aligned
294         {
295             {{"dimaligned", QObject::tr("dimaligned", "dimension - aligned")}},
296             {{"da", QObject::tr("da", "dimension - aligned")}},
297             RS2::ActionDimAligned
298         },
299         //dimension horizontal
300         {
301             {{"dimhorizontal", QObject::tr("dimhorizontal", "dimension - horizontal")}},
302             {{"dh", QObject::tr("dh", "dimension - horizontal")}},
303             RS2::ActionDimLinearHor
304         },
305         //dimension vertical
306         {
307             {{"dimvertical", QObject::tr("dimvertical", "dimension - vertical")}},
308             {{"dv", QObject::tr("dv", "dimension - vertical")}},
309             RS2::ActionDimLinearVer
310         },
311         //dimension linear
312         {
313             {{"dimlinear", QObject::tr("dimlinear", "dimension - linear")}},
314 			{{"dl", QObject::tr("dl", "dimension - linear")},
315              {"dr", QObject::tr("dr", "dimension - linear")}},
316             RS2::ActionDimLinear
317         },
318 	//dimension angular
319 	{
320 		{{"dimangular", QObject::tr("dimangular", "dimension - angular")}},
321 		{{"dan", QObject::tr("dan", "dimension - angular")}},
322 		RS2::ActionDimAngular
323 	},
324 	//dimension radius
325 	{
326 		{{"dimradial", QObject::tr("dimradial", "dimension - radial")}},
327 		{{"dimradius", QObject::tr("dimradius", "dimension - radius")}},
328 		RS2::ActionDimRadial
329 	},
330 	//dimension diameter
331 	{
332 		{{"dimdiametric", QObject::tr("dimdiametric", "dimension - diametric")}},
333 		{{"dimdiameter", QObject::tr("dimdiameter", "dimension - diametric")},
334 		 {"dd", QObject::tr("dd", "dimension - diametric")}},
335 		RS2::ActionDimDiametric
336 	},
337         //dimension leader
338         {
339             {{"dimleader", QObject::tr("dimleader", "dimension - leader")}},
340             {{"ld", QObject::tr("ld", "dimension - leader")}},
341             RS2::ActionDimLeader
342         },
343         //dimension regenerate
344         {
345             {{"dimregen", QObject::tr("dimregen", "dimension - regenerate")}},
346             {},
347             RS2::ActionToolRegenerateDimensions
348         },
349         //snap restrictions
350         {
351             {{"restrictnothing", QObject::tr("restrictnothing", "restrict - nothing")}},
352             {{"rn", QObject::tr("rn", "restrict - nothing")}},
353             RS2::ActionRestrictNothing
354         },
355         //snap orthogonal
356         {
357             {{"restrictorthogonal", QObject::tr("restrictorthogonal", "restrict - orthogonal")}},
358             {{"rr", QObject::tr("rr", "restrict - orthogonal")}},
359             RS2::ActionRestrictOrthogonal
360         },
361         //snap horizontal
362         {
363             {{"restricthorizontal", QObject::tr("restricthorizontal", "restrict - horizontal")}},
364             {{"rh", QObject::tr("rh", "restrict - horizontal")}},
365             RS2::ActionRestrictHorizontal
366         },
367         //snap vertical
368         {
369             {{"restrictvertical", QObject::tr("restrictvertical", "restrict - vertical")}},
370             {{"rv", QObject::tr("rv", "restrict - vertical")}},
371             RS2::ActionRestrictVertical
372         },
373         //move
374         {
375             {{"move", QObject::tr("move", "modify - move (copy)")}},
376             {{"mv", QObject::tr("mv", "modify - move (copy)")}},
377             RS2::ActionModifyMove
378         },
379         //bevel
380         {
381             {{"bevel", QObject::tr("bevel", "modify - bevel")}},
382             {{"bev", QObject::tr("bev", "modify - bevel")},
383             {"ch", QObject::tr("ch", "modify - bevel")}},
384             RS2::ActionModifyBevel
385         },
386         //fillet
387         {
388             {{"fillet", QObject::tr("fillet", "modify - fillet")}},
389             {{"fi", QObject::tr("fi", "modify - fillet")}},
390             RS2::ActionModifyRound
391         },
392         //divide
393         {
394             {{"divide", QObject::tr("divide", "modify - divide (cut)")},
395              {"cut", QObject::tr("cut", "modify - divide (cut)")}},
396             {{"div", QObject::tr("div", "modify - divide (cut)")},
397             {"di", QObject::tr("di", "modify - divide (cut)")}},
398             RS2::ActionModifyCut
399         },
400         //mirror
401         {
402             {{"mirror", QObject::tr("mirror", "modify -  mirror")}},
403             {{"mi", QObject::tr("mi", "modify -  mirror")}},
404             RS2::ActionModifyMirror
405         },
406         //revert
407         {
408             {{"revert", QObject::tr("revert", "modify -  revert direction")}},
409             {{"rev", QObject::tr("rev", "modify -  revert direction")}},
410             RS2::ActionModifyRevertDirection
411         },
412         //rotate
413         {
414             {{"rotate", QObject::tr("rotate", "modify - rotate")}},
415             {{"ro", QObject::tr("ro", "modify - rotate")}},
416             RS2::ActionModifyRotate
417         },
418         //scale
419         {
420             {{"scale", QObject::tr("scale", "modify - scale")}},
421             {{"sz", QObject::tr("sz", "modify - scale")}},
422             RS2::ActionModifyScale
423         },
424         //trim
425         {
426             {{"trim", QObject::tr("trim", "modify - trim (extend)")}},
427             {{"tm", QObject::tr("tm", "modify - trim (extend)")}},
428             RS2::ActionModifyTrim
429         },
430         //trim2
431         {
432             {{"trim2", QObject::tr("trim2", "modify - multi trim (extend)")}},
433             {{"tm2", QObject::tr("tm2", "modify - multi trim (extend)")},
434              {"t2", QObject::tr("t2", "modify - multi trim (extend)")}},
435             RS2::ActionModifyTrim2
436         },
437         //lengthen
438         {
439             {{"lengthen", QObject::tr("lengthen", "modify - lengthen")}},
440             {{"le", QObject::tr("le", "modify - lengthen")}},
441             RS2::ActionModifyTrimAmount
442         },
443         //stretch
444         {
445             {{"stretch", QObject::tr("stretch", "modify - stretch")}},
446             {{"ss", QObject::tr("ss", "modify - stretch")}},
447             RS2::ActionModifyStretch
448         },
449         //delete
450         {
451             {{"delete", QObject::tr("delete", "modify - delete (erase)")}},
452             {{"er", QObject::tr("er", "modify - delete (erase)")},
453              {"del", QObject::tr("del", "modify - delete (erase)")}},
454             RS2::ActionModifyDelete
455 		},
456         //explode
457         {
458             {{"explode", QObject::tr("explode", "explode block/polyline into entities")}},
459             {{"xp", QObject::tr("xp", "explode block/polyline into entities")}},
460             RS2::ActionBlocksExplode
461         },
462         //snap free
463         {
464             {{"snapfree", QObject::tr("snapfree", "snap - free")}},
465             {{"os", QObject::tr("os", "snap - free")},
466             {"sf", QObject::tr("sf", "snap - free")}},
467             RS2::ActionSnapFree
468         },
469         //snap center
470         {
471             {{"snapcenter", QObject::tr("snapcenter", "snap - center")}},
472             {{"sc", QObject::tr("sc", "snap - center")}},
473             RS2::ActionSnapCenter
474         },
475         //snap dist
476         {
477             {{"snapdist", QObject::tr("snapdist", "snap - distance to endpoints")}},
478             {{"sd", QObject::tr("sd", "snap - distance to endpoints")}},
479             RS2::ActionSnapDist
480         },
481         //snap end
482         {
483             {{"snapend", QObject::tr("snapend", "snap - end points")}},
484             {{"se", QObject::tr("se", "snap - end points")}},
485             RS2::ActionSnapEndpoint
486         },
487         //snap grid
488         {
489             {{"snapgrid", QObject::tr("snapgrid", "snap - grid")}},
490             {{"sg", QObject::tr("sg", "snap - grid")}},
491             RS2::ActionSnapGrid
492         },
493         //snap intersection
494         {
495             {{"snapintersection", QObject::tr("snapintersection", "snap - intersection")}},
496             {{"si", QObject::tr("si", "snap - intersection")}},
497             RS2::ActionSnapIntersection
498         },
499         //snap middle
500         {
501             {{"snapmiddle", QObject::tr("snapmiddle", "snap - middle points")}},
502             {{"sm", QObject::tr("sm", "snap - middle points")}},
503             RS2::ActionSnapMiddle
504         },
505         //snap on entity
506         {
507             {{"snaponentity", QObject::tr("snaponentity", "snap - on entity")}},
508             {{"sn", QObject::tr("sn", "snap - on entity")},
509             {"np", QObject::tr("np", "snap - on entity")}},
510             RS2::ActionSnapOnEntity
511         },
512         //set relative zero
513         {
514             {{"setrelativezero", QObject::tr("setrelativezero", "set relative zero position")}},
515             {{"rz", QObject::tr("rz", "set relative zero position")}},
516             RS2::ActionSetRelativeZero
517         },
518         //Select all entities
519         {
520             {{"selectall", QObject::tr("selectall", "Select all entities")}},
521             {{"sa", QObject::tr("sa", "Select all entities")}},
522             RS2::ActionSelectAll
523         },
524         //DeSelect all entities
525         {
526             {{"deselectall", QObject::tr("deselectall", "deselect all entities")}},
527             {{"tn", QObject::tr("tn", "deselect all entities")}},
528             RS2::ActionDeselectAll
529         },
530         //Modify Attributes
531         {
532             {{"modifyattr", QObject::tr("modifyattr", "modify attribute")}},
533             {{"attr", QObject::tr("attr", "modify attribute")},
534             {"ma", QObject::tr("ma", "modify attribute")}},
535             RS2::ActionModifyAttributes
536         },
537         //Modify Properties
538         {
539             {{"properties", QObject::tr("properties", "modify properties")}},
540             {{"prop", QObject::tr("prop", "modify properties")},
541              {"mp", QObject::tr("mp", "modify properties")}},
542             RS2::ActionModifyEntity
543         },
544         //Distance Point to Point
545         {
546             {{"distance", QObject::tr("distance", "distance point to point")}},
547             {{"dist", QObject::tr("dist", "distance point to point")},
548             {"dpp", QObject::tr("dpp", "distance point to point")}},
549             RS2::ActionInfoDist
550 		},
551         //Measure angle
552         {
553             {{"angle", QObject::tr("angle", "measure angle")}},
554             {{"ang", QObject::tr("ang", "measure angle")}},
555             RS2::ActionInfoAngle
556         },
557         //Measure area
558         {
559             {{"area", QObject::tr("area", "measure area")}},
560             {{"ar", QObject::tr("ar", "measure area")}},
561             RS2::ActionInfoArea
562         }
563     };
564 
565     for(auto const& c0: commandList){
566         auto const act=c0.actionType;
567         //add full commands
568         for(auto const& p0: c0.fullCmdList){
569 			if(isCollisionFree(cmdTranslation, p0.first, p0.second))
570 				cmdTranslation[p0.first]=p0.second;
571 			if(isCollisionFree(mainCommands, p0.second, act))
572 				mainCommands[p0.second]=act;
573         }
574         //add short commands
575         for(auto const& p1: c0.shortCmdList){
576 			if(isCollisionFree(cmdTranslation, p1.first, p1.second))
577 				cmdTranslation[p1.first]=p1.second;
578 			if(isCollisionFree(shortCommands, p1.second, act))
579 				shortCommands[p1.second]=act;
580         }
581     }
582 
583     // translations
584     std::vector<std::pair<QString, QString>> transList={
585         {"angle",QObject::tr("angle")},
586         {"dpi",QObject::tr("dpi")},
587         {"close",QObject::tr("close")},
588         {"chord length",QObject::tr("chord length")},
589         {"columns",QObject::tr("columns")},
590         {"columnspacing",QObject::tr("columnspacing")},
591         {"factor",QObject::tr("factor")},
592         {"length",QObject::tr("length")},
593         {"length1",QObject::tr("length1", "bevel/fillet length1")},
594         {"length2",QObject::tr("length2", "bevel/fillet length2")},
595         {"number",QObject::tr("number")},
596         {"radius",QObject::tr("radius")},
597         {"rows",QObject::tr("rows")},
598         {"rowspacing",QObject::tr("rowspacing")},
599         {"through",QObject::tr("through")},
600         {"trim",QObject::tr("trim")},
601 
602     /** following are reversed translation,i.e.,from translated to english **/
603         //not used as command keywords
604     // used in function,checkCommand()
605         {QObject::tr("angle"),"angle"},
606         {QObject::tr("ang", "angle"),"angle"},
607         {QObject::tr("an", "angle"),"angle"},
608 
609         {QObject::tr("center"),"center"},
610         {QObject::tr("cen", "center"),"center"},
611         {QObject::tr("ce", "center"),"center"},
612 
613         {QObject::tr("chord length"),"chord length"},
614     //    {QObject::tr("length", "chord length"),"chord length"},
615         {QObject::tr("cl", "chord length"),"chord length"},
616 
617         {QObject::tr("close"),"close"},
618         {QObject::tr("c", "close"),"close"},
619 
620         {QObject::tr("columns"),"columns"},
621         {QObject::tr("cols", "columns"),"columns"},
622         {QObject::tr("co", "columns"),"columns"},
623 
624         {QObject::tr("columnspacing", "columnspacing for inserts"),"columnspacing"},
625         {QObject::tr("colspacing", "columnspacing for inserts"),"columnspacing"},
626         {QObject::tr("cs", "columnspacing for inserts"),"columnspacing"},
627 
628         {QObject::tr("factor"),"factor"},
629         {QObject::tr("fact", "factor"),"factor"},
630         {QObject::tr("f", "factor"),"factor"},
631 
632         {QObject::tr("help"),"help"},
633         {QObject::tr("?", "help"),"help"},
634 
635         {QObject::tr("length","length"),"length"},
636         {QObject::tr("len","length"),"length"},
637         {QObject::tr("l","length"),"length"},
638 
639         {QObject::tr("length1","length1"),"length1"},
640         {QObject::tr("len1","length1"),"length1"},
641         {QObject::tr("l1","length1"),"length1"},
642 
643         {QObject::tr("length2","length2"),"length2"},
644         {QObject::tr("len2","length2"),"length2"},
645         {QObject::tr("l2","length2"),"length2"},
646 
647         {QObject::tr("number","number"),"number"},
648         {QObject::tr("num","number"),"number"},
649         {QObject::tr("n","number"),"number"},
650 
651         {QObject::tr("radius"),"radius"},
652         {QObject::tr("ra","radius"),"radius"},
653 
654         {QObject::tr("reversed","reversed"),"reversed"},
655         {QObject::tr("rev","reversed"),"reversed"},
656         {QObject::tr("rev","reversed"),"reversed"},
657 
658         {QObject::tr("row", "row"),"row"},
659 
660         {QObject::tr("rowspacing", "rowspacing for inserts"),"rowspacing"},
661         {QObject::tr("rs","rowspacing for inserts"),"rowspacing"},
662 
663         {QObject::tr("text"),"text"},
664         {QObject::tr("t","text"),"text"},
665 
666         {QObject::tr("through"),"through"},
667         {QObject::tr("t","through"),"through"},
668 
669         {QObject::tr("undo"),"undo"},
670         {QObject::tr("u","undo"),"undo"},
671 
672         {QObject::tr("redo"),"redo"},
673         {QObject::tr("r","redo"),"redo"},
674 
675         {QObject::tr("back"),"back"},
676         {QObject::tr("b","back"),"back"},
677         //printer preview
678 		{QObject::tr("bw"), "blackwhite"},
679 		{QObject::tr("blackwhite"), "blackwhite"},
680 		{QObject::tr("color"), "color"},
681 		{QObject::tr("paperoffset"),"paperoffset"},
682         {QObject::tr("graphoffset"),"graphoffset"}
683 };
684     for(auto const& p: transList){
685        cmdTranslation[p.first] = p.second;
686     }
687 }
688 
689 /**
690  * Read existing alias file or create one new.
691  * In OS_WIN32 "c:\documents&settings\<user>\local configuration\application data\LibreCAD\librecad.alias"
692  * In OS_MAC "/Users/<user>/Library/Application Support/LibreCAD/librecad.alias"
693  * In OS_LINUX "/home/<user>/.local/share/data/LibreCAD/librecad.alias"
694  */
updateAlias()695 void RS_Commands::updateAlias(){
696     QString aliasName = RS_SYSTEM->getAppDataDir();
697     if (aliasName.isEmpty())
698         return;
699     aliasName += "/librecad.alias";
700 //    qDebug()<<"alias file:\t"<<aliasName;
701     QFile f(aliasName);
702     QString line;
703     std::map<QString, QString> aliasList;
704     if (f.exists()) {
705 
706         //alias file exists, read user defined alias
707         if (f.open(QIODevice::ReadOnly)) {
708 //        qDebug()<<"alias File: "<<aliasName;
709             QTextStream ts(&f);
710             //check if is empty file or not alias file
711             //            if(!line.isNull() || line == "#LibreCAD alias v1") {
712             //                while (!ts.atEnd())
713             while(!ts.atEnd())
714             {
715                 line=ts.readLine().trimmed();
716                 if (line.isEmpty() || line.at(0)=='#' ) continue;
717                 // Read alias
718                 QStringList txtList = line.split(QRegExp(R"(\s)"),QString::SkipEmptyParts);
719                 if (txtList.size()> 1) {
720 //                    qDebug()<<"reading: "<<txtList.at(0)<<"\t"<< txtList.at(1);
721                     aliasList[txtList.at(0)]=txtList.at(1);
722                 }
723             }
724         }
725     } else {
726     //alias file do no exist, create one with translated shortCommands
727         if (f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
728             QTextStream ts(&f);
729             ts << "#LibreCAD alias v1" << endl << endl;
730             ts << "# lines starting with # are comments" << endl;
731             ts << "# format are:" << endl;
732             ts << R"(# <alias>\t<command-untranslated>)" << endl;
733             ts << "# example"<<endl;
734             ts << "# l\tline"<<endl<<endl;
735             for(auto const& p: shortCommands){
736                 auto const act=p.second;
737                 for(auto const& pCmd: mainCommands){
738                     if(pCmd.second==act){
739                         ts<<p.first<<'\t'<<pCmd.first<<endl;
740                         break;
741                     }
742                 }
743             }
744             ts.flush();
745         }
746 
747     }
748     //update alias file with non present commands
749 //RLZ: to be written
750 
751     //add alias to shortCommands
752     for(auto const& p: aliasList){
753         if(shortCommands.count(p.first)) continue;
754         if(mainCommands.count(p.first)) continue;
755         if(mainCommands.count(p.second)){
756             RS_DEBUG->print("adding command alias: %s\t%s\n", p.first.toStdString().c_str(), p.second.toStdString().c_str());
757             shortCommands[p.first]=mainCommands[p.second];
758         }else if(cmdTranslation.count(p.second)){
759             RS_DEBUG->print("adding command alias: %s\t%s\n", p.first.toStdString().c_str(), cmdTranslation[p.second].toStdString().c_str());
760             shortCommands[p.first]=mainCommands[cmdTranslation[p.second]];
761         }
762     }
763     f.close();
764 }
765 
766 
767 /**
768  * Tries to complete the given command (e.g. when tab is pressed).
769  */
complete(const QString & cmd)770 QStringList RS_Commands::complete(const QString& cmd) {
771     QStringList ret;
772     for(auto const& p: mainCommands){
773         if(p.first.startsWith(cmd, Qt::CaseInsensitive)){
774             ret << p.first;
775         }
776     }
777     ret.sort();
778 
779     return ret;
780 }
781 
782 
783 
784 /**
785  * @return Command for triggering the given action in the currently chosen
786  * language for commands.
787  *
788  * @param action ID of the action who's command will be returned.
789  * @param num Number of the command. There might be multiple commands
790  *            for the same action (e.g. 'line' and 'l')
791  *
792  * @return The translated command.
793  */
cmdToAction(const QString & cmd,bool verbose)794 RS2::ActionType RS_Commands::cmdToAction(const QString& cmd, bool verbose) {
795     QString full = cmd.toLower();
796     RS2::ActionType ret = RS2::ActionNone;
797 
798         // find command:
799 //	RS2::ActionType* retPtr = mainCommands.value(cmd);
800         if ( mainCommands.count(cmd) ) {
801                 ret = mainCommands[cmd];
802         } else if ( shortCommands.count(cmd) ) {
803                 ret = shortCommands[cmd];
804 		} else
805 			return ret;
806 
807 		if (!verbose) return ret;
808 		// find full command to confirm to user:
809 		for(auto const& p: mainCommands){
810 			if(p.second==ret){
811 				RS_DEBUG->print("RS_Commands::cmdToAction: commandMessage");
812 				RS_DIALOGFACTORY->commandMessage(QObject::tr("Command: %1 (%2)").arg(full).arg(p.first));
813 				//                                        RS_DialogFactory::instance()->commandMessage( QObject::tr("Command: %1").arg(full));
814 				RS_DEBUG->print("RS_Commands::cmdToAction: "
815 								"commandMessage: ok");
816 				return ret;
817 			}
818 		}
819 		RS_DEBUG->print(QObject::tr("RS_Commands:: command not found: %1").arg(full).toStdString().c_str());
820 		return ret;
821 }
822 
823 /**
824  * Gets the action for the given keycode. A keycode is a sequence
825  * of key-strokes that is entered like hotkeys.
826  */
keycodeToAction(const QString & code)827 RS2::ActionType RS_Commands::keycodeToAction(const QString& code) {
828 	if(code.size() < 1)
829 		return RS2::ActionNone;
830 
831 	QString c;
832 
833     if(!(code.startsWith(FnPrefix) ||
834          code.startsWith(AltPrefix) ||
835          code.startsWith(MetaPrefix))) {
836     	if(code.size() < 1 || code.contains(QRegExp("^[a-z].*",Qt::CaseInsensitive)) == false )
837 			 return RS2::ActionNone;
838 	    c = code.toLower();
839 	} else {
840 		c = code;
841 	}
842 
843 
844 //    std::cout<<"regex: "<<qPrintable(c)<<" matches: "<< c.contains(QRegExp("^[a-z].*",Qt::CaseInsensitive))<<std::endl;
845 //    std::cout<<"RS2::ActionType RS_Commands::keycodeToAction("<<qPrintable(c)<<")"<<std::endl;
846 
847     auto it = shortCommands.find(c);
848 
849     if( it == shortCommands.end() ) {
850 
851         //not found, searching for main commands
852         it = mainCommands.find(c);
853         if( it == mainCommands.end() ){
854 //			RS_DIALOGFACTORY->commandMessage(QObject::tr("Command not found: %1").arg(c));
855             return RS2::ActionNone;
856         }
857     }
858     //found
859 	RS_DIALOGFACTORY->commandMessage(QObject::tr("Accepted keycode: %1").arg(c));
860     //fixme, need to handle multiple hits
861     return it->second;
862 }
863 
864 
865 /**
866  * @return translated command for the given English command.
867  */
command(const QString & cmd)868 QString RS_Commands::command(const QString& cmd) {
869     auto it= instance()->cmdTranslation.find(cmd);
870     if(it != instance()->cmdTranslation.end()){
871         return instance()->cmdTranslation[cmd];
872     }
873 	RS_DIALOGFACTORY->commandMessage(QObject::tr("Command not found: %1").arg(cmd));
874     RS_DEBUG->print(RS_Debug::D_WARNING,
875                 "RS_Commands::command: command '%s' unknown", cmd.toLatin1().data());
876     return "";
877 }
878 
879 
880 
881 /**
882  * Checks if the given string 'str' matches the given command 'cmd' for action
883  * 'action'.
884  *
885  * @param cmd The command we want to check for (e.g. 'angle').
886  * @param action The action which wants to know.
887  * @param str The string typically entered by the user.
888  */
checkCommand(const QString & cmd,const QString & str,RS2::ActionType)889 bool RS_Commands::checkCommand(const QString& cmd, const QString& str,
890                                RS2::ActionType /*action*/) {
891 
892 	QString const& strl = str.toLower();
893 	QString const& cmdLower = cmd.toLower();
894     auto it = instance()->cmdTranslation.find(cmdLower);
895     if(it != instance()->cmdTranslation.end()){
896 		RS2::ActionType type0=instance()->cmdToAction(it->second, false);
897         if( type0  != RS2::ActionNone ) {
898             return  type0 ==instance()->cmdToAction(strl);
899         }
900     }
901 
902     it =  instance()->cmdTranslation.find(strl);
903     if(it !=  instance()->cmdTranslation.end()) return it->second == cmdLower;
904     return false;
905 }
906 
907 
908 /**
909  * @return the local translation for "Commands available:".
910  */
msgAvailableCommands()911 QString RS_Commands::msgAvailableCommands() {
912     return QObject::tr("Available commands:");
913 }
914 
915 /**
916  * @brief extractCliCal, filter cli calculator math expression
917  * @param cmd, cli string
918  * @return math expression for RS_Math:eval();
919  */
filterCliCal(const QString & cmd)920 QString RS_Commands::filterCliCal(const QString& cmd)
921 {
922 
923     QString str=cmd.trimmed();
924     const QRegExp calCmd(R"(^(cal|calculate))");
925     if(!(str.contains(calCmd)
926          || str.startsWith(QObject::tr("cal","command to trigger cli calculator"), Qt::CaseInsensitive)
927          || str.startsWith(QObject::tr("calculate","command to trigger cli calculator"), Qt::CaseInsensitive)
928                            )) {
929         return QString();
930     }
931     int index=str.indexOf(QRegExp(R"(\s)"));
932     bool spaceFound=(index>=0);
933     str=str.mid(index);
934     index=str.indexOf(QRegExp(R"(\S)"));
935     if(!(spaceFound && index>=0)) return QString();
936     str=str.mid(index);
937     return str;
938 }
939 
940 // EOF
941