Command.cpp 13.8 KB
Newer Older
1
/*
2
 * Copyright (c) 2013-2015 Lukáš Tvrdý <lukast.dev@gmail.com>
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include <Command.h>
#include <QString>
#include <QStringList>
#include <QChar>
#include <QList>

Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
25
#include <kis_debug.h>
26 27
#include <kis_gmic_parser.h>

28 29 30 31 32 33 34
#include <QWidget>
#include <QGridLayout>
#include <QLabel>
#include <qslider.h>
#include <QSpinBox>
#include <QComboBox>
#include <QStringListModel>
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61

#include <Parameter.h>

Command::Command(Component* parent): m_parent(parent)
{
}

Command::~Command()
{

}


void Command::processCommandName(const QString& line)
{
    QStringList splittedLine = line.split(":");
    Q_ASSERT(splittedLine.size() == 2);

    QString commandName = splittedLine.at(0);
    setName(commandName.remove(0, GIMP_COMMENT.size()).trimmed());

    QStringList commands = splittedLine[1].split(",");
    Q_ASSERT(commands.size() == 2);

    m_command = commands.at(0).trimmed();
    m_commandPreview = commands.at(1).trimmed();

62 63 64 65 66 67 68 69
    QStringList splitted = m_commandPreview.split("(");
    if (splitted.size() == 2)
    {
        m_commandPreview = splitted.at(0);
        m_commandPreviewZoom = splitted.at(1);
        m_commandPreviewZoom.chop(1);
    }

70 71 72 73 74
}


// sep = separator(),
// Preview type = choice("Full","Forward horizontal","Forward vertical","Backward horizontal","Backward vertical","Duplicate horizontal","Duplicate vertical")
Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
75
QStringList Command::breakIntoTokens(const QString &line, bool &lastTokenEnclosed)
76 77
{
    QStringList result;
78
    lastTokenEnclosed = false;
79 80
    int index = 0;
    int lastIndex = 0;
Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
81 82
    while (index < line.size())
    {
83
        // skip parameter name, find index of =
84 85 86 87 88 89 90 91 92 93 94
        index = line.indexOf("=", index);

        if (index == -1)
        {
            break;
        }

        // point to next character
        index++;

        // eat all spaces
95
        index = skipWhitespace(line, index);
96

97
        // skip type-definition name e.g. "int", "_note", etc.
Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
98
        const QChar underscore('_');
99
        int helperIndex = index;
Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
100 101 102 103 104 105 106 107 108 109 110 111
        while (helperIndex < line.size())
        {
            const QChar c = line.at(helperIndex);
            if (c.isLetter() || c == underscore)
            {
                helperIndex++;
            }
            else
            {
                break;
            }

112 113
        }

Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
114 115 116
        QString typeName = line.mid(index, helperIndex - index);
        if (typeName.startsWith(underscore))
        {
117
            typeName.remove(0, 1);
Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
118
        }
119

120
        if (!Parameter::isTypeDefined(typeName))
Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
121 122
        {
            dbgPlugins << "Unknown type" << typeName << line;
123 124
        }

Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
125
        index = helperIndex;
126

127 128 129 130
        // skip all whitespaces, e.g. note = note ("Example")
        index = skipWhitespace(line, index);

        // determine separator
131 132 133
        // Type separators '()' can be replaced by '[]' or '{}' if necessary ...
        QChar delimiter = line.at(index);
        QChar closingdelimiter;
134
        switch (delimiter.toLatin1())
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
        {
            case '(':
            {
                closingdelimiter = ')';
                break;
            }
            case '[':
            {
                closingdelimiter = ']';
                break;
            }
            case '{':
            {
                closingdelimiter = '}';
                break;
            }
            default:
            {
153 154
                dbgPlugins << "Delimiter: " << delimiter << line;
                Q_ASSERT(false);
155 156 157 158
                break;
            }
        }

159 160
        // search for enclosing separator
        while (index < line.size() - 1)
161
        {
162 163 164 165 166 167 168 169 170
            if (line.at(index) != closingdelimiter)
            {
                index++;
            }
            else
            {
                // we found enclosing separator
                break;
            }
171 172
        }

Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
173 174 175
        if (line.at(index) != closingdelimiter)
        {
            lastTokenEnclosed = false;
176
            //dbgPlugins << "Enclosing delimiter not found, trying again" << line.at(index);
177
            break;
Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
178
        }
179
        else
180
        {
181 182 183
            lastTokenEnclosed = true;
            // "," removal only if there are n tokens in one line
            if (lastIndex != 0 )
184
            {
185 186 187 188 189
                // if there are more tokens on one line, they are separated by ",", so remove it here
                if (line.at(lastIndex) == ',')
                {
                    lastIndex++;
                }
190
            }
191 192 193 194 195 196

            QString token = line.mid(lastIndex, index + 1 - lastIndex).trimmed();
            result.append(token);
            lastIndex = index + 1;


197
        }
198
    } // while
199 200 201 202 203

    return result;
}


Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
204
bool Command::processParameter(const QStringList& block)
205
{
Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
206
    QString parameterLine = mergeBlockToLine(block);
207 208
    // remove gimp prefix and " :"
    parameterLine = parameterLine.remove(0, GIMP_COMMENT.size()+2).trimmed();
209 210 211 212 213 214

    /* State: one parameter one line
     * one parameter n lines
     * n parameters one line
    */

215
    // break into parameter tokens
Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
216 217
    bool lastTokenEnclosed = true;
    QStringList tokens = breakIntoTokens(parameterLine, lastTokenEnclosed);
218 219 220



Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
221 222 223
    if (!lastTokenEnclosed)
    {
        // we need more lines of command parameters
224
        //dbgPlugins << "ParameterLine not enclosed";
Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
225 226 227 228
        return false;
    }

    static int unhandledParameters = 0;
229 230 231 232 233

    Parameter * parameter = 0;
    foreach (QString token, tokens)
    {

234 235
        QString paramName;
        QString typeDefinition;
Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
236

237 238
        // determine parameter name and parameter definition
        processToken(token, paramName, typeDefinition);
239

Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
240
        bool showPreviewOnChange = true;
241 242 243 244 245 246
        if (typeDefinition.startsWith("_"))
        {
            showPreviewOnChange = false;
            typeDefinition.remove(0,1);
        }

247
        if (typeDefinition.startsWith(Parameter::PARAMETER_NAMES[Parameter::FLOAT_P]))
248 249
        {
            parameter = new FloatParameter(paramName, showPreviewOnChange);
Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
250
        }
251
        else if (typeDefinition.startsWith(Parameter::PARAMETER_NAMES[Parameter::INT_P]))
252 253
        {
            parameter = new IntParameter(paramName, showPreviewOnChange);
Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
254
        }
255
        else if (typeDefinition.startsWith(Parameter::PARAMETER_NAMES[Parameter::SEPARATOR_P]))
256 257
        {
            parameter = new SeparatorParameter(paramName, showPreviewOnChange);
Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
258
        }
259
        else if (typeDefinition.startsWith(Parameter::PARAMETER_NAMES[Parameter::CHOICE_P]))
260 261
        {
            parameter = new ChoiceParameter(paramName, showPreviewOnChange);
Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
262
        }
263
        else if (typeDefinition.startsWith(Parameter::PARAMETER_NAMES[Parameter::TEXT_P]))
Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
264 265 266
        {
            parameter = new TextParameter(paramName, showPreviewOnChange);
        }
267
        else if (typeDefinition.startsWith(Parameter::PARAMETER_NAMES[Parameter::NOTE_P]))
268 269
        {
            parameter = new NoteParameter(paramName, showPreviewOnChange);
Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
270
        }
271
        else if (typeDefinition.startsWith(Parameter::PARAMETER_NAMES[Parameter::LINK_P]))
Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
272 273 274
        {
            parameter = new LinkParameter(paramName, showPreviewOnChange);
        }
275
        else if (typeDefinition.startsWith(Parameter::PARAMETER_NAMES[Parameter::BOOL_P]))
Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
276 277 278
        {
            parameter = new BoolParameter(paramName, showPreviewOnChange);
        }
279
        else if (typeDefinition.startsWith(Parameter::PARAMETER_NAMES[Parameter::COLOR_P]))
Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
280 281 282
        {
            parameter = new ColorParameter(paramName, showPreviewOnChange);
        }
283
        else if (typeDefinition.startsWith(Parameter::PARAMETER_NAMES[Parameter::FOLDER_P]))
Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
284 285 286
        {
            parameter = new FolderParameter(paramName, showPreviewOnChange);
        }
287
        else if (typeDefinition.startsWith(Parameter::PARAMETER_NAMES[Parameter::FILE_P]))
Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
288 289 290
        {
            parameter = new FileParameter(paramName, showPreviewOnChange);
        }
291
        else if (typeDefinition.startsWith(Parameter::PARAMETER_NAMES[Parameter::CONST_P]))
292 293 294
        {
            parameter = new ConstParameter(paramName, showPreviewOnChange);
        }
295
        else if (typeDefinition.startsWith(Parameter::PARAMETER_NAMES[Parameter::BUTTON_P]))
296 297 298
        {
            parameter = new ButtonParameter(paramName, showPreviewOnChange);
        }
Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
299 300 301
        else
        {
            unhandledParameters++;
Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
302
            dbgPlugins << "Unhandled parameter " << unhandledParameters << parent()->name() << "\\" << name() << paramName << typeDefinition;
303 304 305 306 307 308 309 310 311 312 313 314
        }

        if (parameter)
        {
            parameter->parseValues(typeDefinition);
            m_parameters.append(parameter);
            parameter = 0;
        }


    }
    // let's ignore tokens
Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
315
    return true;
316 317 318 319 320 321 322 323 324 325 326
}


void Command::add(Component* c)
{
    Q_UNUSED(c);
}


void Command::print(int level)
{
Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
327 328 329 330 331
    for(int j=0; j < level; ++j)
    {
        dbgPlugins << "\t";
    }
    dbgPlugins << "Command : " << qPrintable(name());
332 333 334

    foreach(Parameter * p, m_parameters)
    {
Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
335 336 337 338
        for(int j=0; j < level+1; ++j)
        {
            dbgPlugins << "\t";
        }
339 340
        QString str = p->toString();
        str.truncate(30);
Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
341
        dbgPlugins << str;
342 343 344
    }
}

345
Component* Command::child(int index) const
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
{
    Q_UNUSED(index);
    return 0;
}

Component* Command::parent()
{
    return m_parent;
}

int Command::row() const
{
    if (m_parent)
    {
        m_parent->indexOf(const_cast<Command *>(this));
    }
    return 0;
}

QVariant Command::data(int column)
{
367
    Q_UNUSED(column);
368 369 370 371 372 373 374 375 376 377 378 379 380
    Q_ASSERT(column == 0);
    return name();
}

int Command::childCount() const
{
    return 0;
}

int Command::columnCount() const
{
    return 1;
}
381 382 383 384


void Command::writeConfiguration(KisGmicFilterSetting* setting)
{
Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
385
    // example: -gimp_poster_edges 20,60,5,0,10,0,0
386
    QString command = "-" + m_command + " ";
387
    QString commandPreview = "-" + m_commandPreview + " ";
388 389 390 391 392
    foreach(Parameter * p, m_parameters)
    {
        if (!p->value().isNull())
        {
            command.append(p->value() +",");
393
            commandPreview.append(p->value() +",");
394 395 396
        }
        else
        {
Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
397 398 399
            if (!p->isPresentationalOnly())
            {
                // implement for given parameter value()!
Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
400
                dbgPlugins << "UNHANDLED command parameter: " << p->m_name << p->toString();
Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
401
            }
402 403 404 405 406 407 408 409
        }
    }

    if (command.endsWith(","))
    {
        command.chop(1);
    }

410 411 412 413 414
    if (commandPreview.endsWith(","))
    {
        commandPreview.chop(1);
    }

415
    setting->setGmicCommand(command);
416
    setting->setPreviewGmicCommand(commandPreview);
417
}
Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
418 419 420 421 422 423 424 425 426 427 428 429 430 431

QString Command::mergeBlockToLine(const QStringList& block)
{
    Q_ASSERT(block.size() > 0);
    if (block.size() == 1)
    {
        return block.at(0);
    }

    QString result = block.at(0);
    for (int i = 1; i < block.size(); i++)
    {
        QString nextLine = block.at(i);
        nextLine = nextLine.remove(0, GIMP_COMMENT.size()+2).trimmed();
Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
432
        result = result + nextLine;
Lukáš Tvrdý's avatar
Lukáš Tvrdý committed
433 434 435
    }
    return result;
}
436 437 438 439 440 441 442 443 444

void Command::reset()
{
    foreach(Parameter * p, m_parameters)
    {
        p->reset();
    }

}
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478


bool Command::processToken(const QString& token, QString& parameterName, QString& parameterDefinition)
{
        // determine parameter name and parameter definition
        // dbgPlugins << "Processing token " << token;

        QString trimedToken = token.trimmed();
        // '=' separates parameter name and parameter definition
        // parameter definition can contain '=' so avoid split(") here
        int firstSeparatorIndex = trimedToken.indexOf("=");
        Q_ASSERT(firstSeparatorIndex != -1);
        parameterName = trimedToken.left(firstSeparatorIndex).trimmed();
        parameterDefinition = trimedToken.mid(firstSeparatorIndex + 1).trimmed();
        //dbgPlugins << ppVar(parameterName);
        //dbgPlugins << ppVar(parameterDefinition);
        return true;
}

int Command::skipWhitespace(const QString& line, int index)
{
    while (index < line.size())
    {
        if (line.at(index).isSpace())
        {
            index++;
        }
        else
        {
            break;
        }
    }
    return index;
}
479 480 481 482 483 484 485 486 487 488

void Command::setParameter(const QString& name, const QString& value)
{
    for (int i = 0; i < m_parameters.size(); i++)
    {
        if (m_parameters.at(i)->name() == name)
        {
            m_parameters[i]->setValue(value);
        }
    }
489 490 491 492 493 494 495 496 497 498 499
}

QString Command::parameter(const QString &name) const
{
    for (int i = 0; i < m_parameters.size(); i++)
    {
        if (m_parameters.at(i)->name() == name)
        {
            return m_parameters.at(i)->value();
        }
    }
500

501
    return QString();
502
}
503

504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536
bool Command::hasParameterName(const QString& paramName, const QString& paramType)
{
    Parameter::ParameterType type = Parameter::INVALID_P;
    if (!paramType.isEmpty())
    {
        type = Parameter::nameToType(paramType);
    }

    for (int i = 0; i < m_parameters.size(); i++)
    {
        Parameter * currentParameter = m_parameters.at(i);
        if (currentParameter->name() == paramName)
        {
            // if not empty, we check type also
            if (!paramType.isEmpty())
            {
                if (currentParameter->m_type == type)
                {
                    return true;
                }
                else
                {
                    qDebug() << "Ignoring type " << currentParameter->m_type;
                }
            }
            else
            {
                return true;
            }
        }
    }
    return false;
}