File indexing completed on 2024-04-28 03:40:30
0001 /* 0002 SPDX-FileCopyrightText: 2002 Jeff Roush <jeff@mousetool.com> 0003 SPDX-FileCopyrightText: 2003 Olaf Schmidt <ojschmidt@kde.org> 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "mtstroke.h" 0008 #include "kmousetool.h" 0009 #include <iostream> 0010 0011 // these are for locating the stroke information file 0012 0013 // #include <string> 0014 #include <QStandardPaths> 0015 0016 using namespace std; 0017 0018 const int MTStroke::DontClick = -1; 0019 const int MTStroke::bumped_mouse = 0; 0020 const int MTStroke::normal = 1; 0021 const int MTStroke::RightClick = 2; 0022 const int MTStroke::DoubleClick = 3; 0023 const int MTStroke::circle = 4; 0024 const int MTStroke::LowerLeftStroke = 5; 0025 const int MTStroke::UpperRightStroke = 6; 0026 const int MTStroke::LowerRightStroke = 7; 0027 const int MTStroke::UpperLeftStroke = 8; 0028 0029 int MTStroke::delta_xy = 10; 0030 0031 const int MTStroke::min_num_points = 5; 0032 0033 Pt MTStroke::LowerLeft(0, 0); 0034 Pt MTStroke::LowerRight(0, 0); 0035 Pt MTStroke::UpperLeft(0, 0); 0036 Pt MTStroke::UpperRight(0, 0); 0037 0038 MTStroke::MTStroke() 0039 { 0040 readSequence(); 0041 } 0042 MTStroke::~MTStroke() 0043 { 0044 } 0045 0046 // add the new point, but only if it's not the same as the previous point. 0047 void MTStroke::addPt(int x, int y) 0048 { 0049 if (points.size() == 0) { 0050 points.push_back(Pt(x, y)); 0051 } else { 0052 Pt pt(x, y); 0053 if (!pt.sameAs(points[points.size() - 1])) { 0054 points.push_back(Pt(x, y)); 0055 } 0056 } 0057 } 0058 0059 /* 0060 * Loop over all the strokes; 0061 * return true if the given point is included 0062 */ 0063 bool MTStroke::pointsContain(Pt pt) 0064 { 0065 std::vector<Pt>::iterator pos; 0066 for (pos = points.begin(); pos < points.end(); ++pos) { 0067 if (pt.x == pos->x && pt.y == pos->y) 0068 return true; 0069 } 0070 return false; 0071 } 0072 0073 int MTStroke::getStrokeType() 0074 { 0075 int size = points.size(); 0076 0077 // If the mouse moved just a bit, it was probably bumped. Don't click. 0078 if (size < min_num_points) 0079 return normal; 0080 // return bumped_mouse; 0081 0082 Pt lastPoint = points[points.size() - 1]; 0083 0084 // If the mouse is pausing in a corner, then the user is either in the middle of a 0085 // stroke, or wants to rest the mouse. Don't click. 0086 if (lastPoint.sameAs(LowerLeft) || lastPoint.sameAs(LowerRight) || lastPoint.sameAs(UpperLeft) || lastPoint.sameAs(UpperRight)) 0087 return DontClick; 0088 0089 // If the mouse visited a corner... 0090 if (pointsContain(LowerLeft)) { 0091 reset(); 0092 return LowerLeftStroke; 0093 } 0094 if (pointsContain(UpperRight)) { 0095 reset(); 0096 return UpperRightStroke; 0097 } 0098 scale(); 0099 0100 std::map<std::string, int>::iterator keypos = sequenceMap.find(sequence); 0101 if (keypos == sequenceMap.end()) { 0102 reset(); 0103 return normal; 0104 } 0105 reset(); 0106 // return RightClick; 0107 return keypos->second; 0108 } 0109 0110 void MTStroke::scale() 0111 { 0112 getExtent(); 0113 int deltax = stroke_maxx - stroke_minx; 0114 int deltay = stroke_maxy - stroke_miny; 0115 int delta = max(deltax, deltay); 0116 int scale = (int)delta / 2; 0117 0118 std::vector<Pt>::iterator pos; 0119 for (pos = points.begin(); pos < points.end(); ++pos) { 0120 // add an extra (scale/2) because the divide rounds _down_, and we want to 0121 // round _up_ or _down_, depending on which is closer. 0122 pos->x = (int)(pos->x - stroke_minx + scale / 2) / scale; 0123 pos->y = (int)(pos->y - stroke_miny + scale / 2) / scale; 0124 0125 // now, get the integer representing this position and add it to the stroke sequence 0126 int n = 3 * pos->y + pos->x + 1; 0127 int index = sequence.size() - 1; 0128 n += '0'; 0129 if (index == -1) 0130 sequence += n; 0131 else if (n != sequence[index]) 0132 sequence += n; 0133 } 0134 } 0135 0136 int MTStroke::max(int n, int m) 0137 { 0138 if (n > m) 0139 return n; 0140 return m; 0141 } 0142 0143 /* 0144 * Find the bounding rectangle for the stroke 0145 */ 0146 void MTStroke::getExtent() 0147 { 0148 stroke_minx = UpperRight.x; 0149 stroke_maxx = 0; 0150 stroke_miny = LowerLeft.y; 0151 stroke_maxy = 0; 0152 0153 std::vector<Pt>::iterator pos; 0154 for (pos = points.begin(); pos < points.end(); ++pos) { 0155 if (stroke_minx > pos->x) 0156 stroke_minx = pos->x; 0157 if (stroke_maxx < pos->x) 0158 stroke_maxx = pos->x; 0159 if (stroke_miny > pos->y) 0160 stroke_miny = pos->y; 0161 if (stroke_maxy < pos->y) 0162 stroke_maxy = pos->y; 0163 } 0164 } 0165 0166 // test if strokefile exists; if not, create it from defaults. 0167 // if unable to create it, 0168 bool MTStroke::readSequence() 0169 { 0170 QString strokefilename; 0171 strokefilename = QStandardPaths::locate(QStandardPaths::ConfigLocation, QStringLiteral("kmousetool_strokes.txt")); 0172 if (strokefilename.isEmpty()) { 0173 // make default 0174 if (sequenceMap.size() == 0) 0175 makeDefaultSequenceMap(); 0176 writeSequence(); 0177 return false; 0178 } 0179 ifstream infile(strokefilename.toLatin1().constData()); 0180 if (!infile) { 0181 // make default 0182 if (sequenceMap.size() == 0) 0183 makeDefaultSequenceMap(); 0184 writeSequence(); 0185 return false; 0186 } 0187 0188 while (!infile.eof()) { 0189 string str; 0190 infile >> str; 0191 if (str[0] == '#') 0192 readToEndOfLine(infile); 0193 else { 0194 // get the associated action 0195 string str2; 0196 infile >> str2; 0197 int n = str2[0] - '0'; // the action is a single integer digit; convert it to an int 0198 sequenceMap[string(str)] = n; 0199 } 0200 } 0201 return true; 0202 } 0203 0204 bool MTStroke::writeSequence() 0205 { 0206 QString strokefilename; 0207 strokefilename = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QLatin1String("/kmousetool_strokes.txt"); 0208 0209 ofstream outfile(strokefilename.toLatin1().constData(), ios::out); 0210 if (!outfile) { 0211 return false; 0212 } 0213 0214 outfile << "# This file contains definitions for valid strokes for KMouseTool\n"; 0215 outfile << "# To make sense of the numbers: \n"; 0216 outfile << "# The mouse path begins and ends when the mouse is paused.\n"; 0217 outfile << "# Imagine a square enclosing the path.\n"; 0218 outfile << "# Divide the square into 9 boxes, and number them like so:\n"; 0219 outfile << "# 1 2 3\n"; 0220 outfile << "# 4 5 6\n"; 0221 outfile << "# 7 8 9\n"; 0222 outfile << "# \n"; 0223 outfile << "# The mouse path can then be described by a sequence of numbers:\n"; 0224 outfile << "# for example, \"12321\" describes the mouse moving from left to right and back.\n"; 0225 outfile << "# This general scheme follows libstroke (https://directory.fsf.org/wiki/LibStroke)\n"; 0226 outfile << "# although it was reimplemented from scratch for KMouseTool.\n"; 0227 outfile << "\n"; 0228 outfile << "# For each stroke recognized, provide an integer describing an action\n"; 0229 outfile << "# KMouseTool can take. At the moment, valid actions are:\n"; 0230 outfile << "# -1 Do not click\n"; 0231 outfile << "# 1 Normal click (use Smart Drag if that's enabled)\n"; 0232 outfile << "# 2 Right click\n"; 0233 outfile << "# 3 Double click\n"; 0234 outfile << "\n"; 0235 outfile << "#Stroke\tAction\n"; 0236 std::map<std::string, int>::iterator pos = sequenceMap.begin(); 0237 while (pos != sequenceMap.end()) { 0238 outfile << pos->first << "\t" << pos->second << "\n"; 0239 pos++; 0240 } 0241 return true; 0242 } 0243 0244 void MTStroke::makeDefaultSequenceMap() 0245 { 0246 sequenceMap[string("12321")] = RightClick; 0247 sequenceMap[string("1321")] = RightClick; 0248 sequenceMap[string("1231")] = RightClick; 0249 sequenceMap[string("131")] = RightClick; 0250 0251 sequenceMap[string("32123")] = DoubleClick; 0252 sequenceMap[string("3213")] = DoubleClick; 0253 sequenceMap[string("3123")] = DoubleClick; 0254 sequenceMap[string("313")] = DoubleClick; 0255 /* 0256 sequenceMap[ string("") ] = ; 0257 sequenceMap[ string("") ] = ; 0258 sequenceMap[ string("") ] = ; 0259 sequenceMap[ string("") ] = ; 0260 sequenceMap[ string("") ] = ; 0261 */ 0262 } 0263 0264 void MTStroke::readToEndOfLine(ifstream &infile) 0265 { 0266 char ch = 'a'; 0267 while (ch != '\n') 0268 infile.get(ch); 0269 }