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 }