FreeCAD

Форк
0
/
DlgKeyboardImp.cpp 
573 строки · 22.3 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2005 Werner Mayer <wmayer[at]users.sourceforge.net>     *
3
 *                                                                         *
4
 *   This file is part of the FreeCAD CAx development system.              *
5
 *                                                                         *
6
 *   This library is free software; you can redistribute it and/or         *
7
 *   modify it under the terms of the GNU Library General Public           *
8
 *   License as published by the Free Software Foundation; either          *
9
 *   version 2 of the License, or (at your option) any later version.      *
10
 *                                                                         *
11
 *   This library  is distributed in the hope that it will be useful,      *
12
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
13
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
14
 *   GNU Library General Public License for more details.                  *
15
 *                                                                         *
16
 *   You should have received a copy of the GNU Library General Public     *
17
 *   License along with this library; see the file COPYING.LIB. If not,    *
18
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
19
 *   Suite 330, Boston, MA  02111-1307, USA                                *
20
 *                                                                         *
21
 ***************************************************************************/
22

23

24
#include "PreCompiled.h"
25
#include <boost/signals2/connection.hpp>
26
#ifndef _PreComp_
27
# include <QAction>
28
# include <QHeaderView>
29
# include <QMessageBox>
30
# include <QTimer>
31
#endif
32

33
#include <Base/Parameter.h>
34
#include <Base/Tools.h>
35
#include <Base/Console.h>
36

37
#include "DlgKeyboardImp.h"
38
#include "ui_DlgKeyboard.h"
39
#include "Action.h"
40
#include "Application.h"
41
#include "BitmapFactory.h"
42
#include "Command.h"
43
#include "Window.h"
44
#include "PrefWidgets.h"
45
#include "ShortcutManager.h"
46
#include "CommandCompleter.h"
47

48

49
using namespace Gui::Dialog;
50

51
namespace Gui { namespace Dialog {
52
using GroupMap = std::vector< std::pair<QLatin1String, QString> >;
53

54
struct GroupMap_find {
55
    const QLatin1String& item;
56
    explicit GroupMap_find(const QLatin1String& item) : item(item) {}
57
    bool operator () (const std::pair<QLatin1String, QString>& elem) const
58
    {
59
        return elem.first == item;
60
    }
61
};
62
}
63
}
64

65
/* TRANSLATOR Gui::Dialog::DlgCustomKeyboardImp */
66

67
/**
68
 *  Constructs a DlgCustomKeyboardImp which is a child of 'parent', with the
69
 *  name 'name' and widget flags set to 'f'
70
 *
71
 *  The dialog will by default be modeless, unless you set 'modal' to
72
 *  true to construct a modal dialog.
73
 */
74
DlgCustomKeyboardImp::DlgCustomKeyboardImp( QWidget* parent  )
75
  : CustomizeActionPage(parent)
76
  , ui(new Ui_DlgCustomKeyboard)
77
  , firstShow(true)
78
{
79
    ui->setupUi(this);
80
    setupConnections();
81

82
    // Force create actions for all commands with shortcut to register with ShortcutManager
83
    for (auto cmd : Application::Instance->commandManager().getAllCommands()) {
84
        if (cmd->getShortcut().size())
85
            cmd->initAction();
86
    }
87
    QObject::connect(ShortcutManager::instance(), &ShortcutManager::shortcutChanged, this,
88
        [](const char *cmdName) {
89
            if (auto cmd = Application::Instance->commandManager().getCommandByName(cmdName))
90
                cmd->initAction();
91
        });
92

93
    conn = initCommandWidgets(ui->commandTreeWidget,
94
                              nullptr,
95
                              ui->categoryBox,
96
                              ui->editCommand,
97
                              ui->assignedTreeWidget,
98
                              ui->buttonUp,
99
                              ui->buttonDown,
100
                              ui->editShortcut,
101
                              ui->accelLineEditShortcut);
102

103
    ui->shortcutTimeout->onRestore();
104
    QTimer *timer = new QTimer(this);
105
    QObject::connect(ui->shortcutTimeout, qOverload<int>(&QSpinBox::valueChanged), timer, [=](int) {
106
        timer->start(100);
107
    });
108
    QObject::connect(timer, &QTimer::timeout, [=]() {
109
        ui->shortcutTimeout->onSave();
110
    });
111
}
112

113
/** Destroys the object and frees any allocated resources */
114
DlgCustomKeyboardImp::~DlgCustomKeyboardImp() = default;
115

116
void DlgCustomKeyboardImp::setupConnections()
117
{
118
    connect(ui->categoryBox, qOverload<int>(&QComboBox::activated),
119
            this, &DlgCustomKeyboardImp::onCategoryBoxActivated);
120
    connect(ui->commandTreeWidget, &QTreeWidget::currentItemChanged,
121
            this, &DlgCustomKeyboardImp::onCommandTreeWidgetCurrentItemChanged);
122
    connect(ui->buttonAssign, &QPushButton::clicked,
123
            this, &DlgCustomKeyboardImp::onButtonAssignClicked);
124
    connect(ui->buttonClear, &QPushButton::clicked,
125
            this, &DlgCustomKeyboardImp::onButtonClearClicked);
126
    connect(ui->buttonReset, &QPushButton::clicked,
127
            this, &DlgCustomKeyboardImp::onButtonResetClicked);
128
    connect(ui->buttonResetAll, &QPushButton::clicked,
129
            this, &DlgCustomKeyboardImp::onButtonResetAllClicked);
130
    connect(ui->editShortcut, &AccelLineEdit::textChanged,
131
            this, &DlgCustomKeyboardImp::onEditShortcutTextChanged);
132
}
133

134
void DlgCustomKeyboardImp::initCommandCompleter(QLineEdit *edit,
135
                                                QComboBox *combo,
136
                                                QTreeWidget *commandTreeWidget,
137
                                                QTreeWidgetItem *separatorItem)
138
{
139
    edit->setPlaceholderText(tr("Type to search..."));
140
    auto completer = new CommandCompleter(edit, edit);
141

142
    QObject::connect(completer, &CommandCompleter::commandActivated,
143
        [=](const QByteArray &name) {
144
            CommandManager & cCmdMgr = Application::Instance->commandManager();
145
            Command *cmd = cCmdMgr.getCommandByName(name.constData());
146
            if (!cmd)
147
                return;
148

149
            QString group = QString::fromLatin1(cmd->getGroupName());
150
            int index = combo->findData(group);
151
            if (index < 0)
152
                return;
153
            if (index != combo->currentIndex()) {
154
                QSignalBlocker blocker(combo);
155
                combo->setCurrentIndex(index);
156
                populateCommandList(commandTreeWidget, separatorItem, combo);
157
            }
158
            for (int i=0 ; i<commandTreeWidget->topLevelItemCount(); ++i) {
159
                QTreeWidgetItem *item = commandTreeWidget->topLevelItem(i);
160
                if (item->data(1, Qt::UserRole).toByteArray() == name) {
161
                    commandTreeWidget->setCurrentItem(item);
162
                    return;
163
                }
164
            }
165
        });
166
}
167

168
void DlgCustomKeyboardImp::populateCommandList(QTreeWidget *commandTreeWidget,
169
                                               QTreeWidgetItem *separatorItem,
170
                                               QComboBox *combo) 
171
{
172
    QByteArray current;
173
    if (auto item = commandTreeWidget->currentItem())
174
        current = item->data(1, Qt::UserRole).toByteArray();
175

176
    if (separatorItem) {
177
        commandTreeWidget->takeTopLevelItem(commandTreeWidget->indexOfTopLevelItem(separatorItem));
178
    }
179
    commandTreeWidget->clear();
180
    if (separatorItem) {
181
        commandTreeWidget->addTopLevelItem(separatorItem);
182
    }
183

184
    CommandManager & cCmdMgr = Application::Instance->commandManager();
185
    auto group = combo->itemData(combo->currentIndex(), Qt::UserRole).toByteArray();
186
    auto cmds = group == "All" ? cCmdMgr.getAllCommands()
187
                                : cCmdMgr.getGroupCommands(group.constData());
188
    QTreeWidgetItem *currentItem = nullptr;
189
    for (const Command *cmd : cmds) {
190
        QTreeWidgetItem* item = new QTreeWidgetItem(commandTreeWidget);
191
        item->setText(1, Action::commandMenuText(cmd));
192
        item->setToolTip(1, Action::commandToolTip(cmd));
193
        item->setData(1, Qt::UserRole, QByteArray(cmd->getName()));
194
        item->setSizeHint(0, QSize(32, 32));
195
        if (auto pixmap = cmd->getPixmap())
196
            item->setIcon(0, BitmapFactory().iconFromTheme(pixmap));
197
        item->setText(2, cmd->getShortcut());
198
        if (auto accel = cmd->getAccel())
199
            item->setText(3, QKeySequence(QString::fromLatin1(accel)).toString());
200

201
        if (current == cmd->getName())
202
            currentItem = item;
203
    }
204
    if (currentItem)
205
        commandTreeWidget->setCurrentItem(currentItem);
206
    commandTreeWidget->resizeColumnToContents(2);
207
    commandTreeWidget->resizeColumnToContents(3);
208
}
209

210
boost::signals2::connection
211
DlgCustomKeyboardImp::initCommandList(QTreeWidget *commandTreeWidget,
212
                                      QTreeWidgetItem *separatorItem,
213
                                      QComboBox *combo)
214
{
215
    QStringList labels;
216
    labels << tr("Icon") << tr("Command") << tr("Shortcut") << tr("Default");
217
    commandTreeWidget->setHeaderLabels(labels);
218
    commandTreeWidget->setIconSize(QSize(32, 32));
219
    commandTreeWidget->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
220
    commandTreeWidget->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
221
    commandTreeWidget->header()->setSectionResizeMode(3, QHeaderView::ResizeToContents);
222

223
    populateCommandGroups(combo);
224

225
    // Using a timer to respond to command change for performance, and also
226
    // because macro command may be added before proper initialization (null
227
    // menu text, etc.)
228
    QTimer *timer = new QTimer(combo);
229
    timer->setSingleShot(true);
230

231
    QObject::connect(timer, &QTimer::timeout, [=](){
232
        populateCommandGroups(combo);
233
        populateCommandList(commandTreeWidget, separatorItem, combo);
234
    });
235

236
    QObject::connect(ShortcutManager::instance(), &ShortcutManager::shortcutChanged, timer, [timer]() {
237
        timer->start(100);
238
    });
239

240
    QObject::connect(combo, qOverload<int>(&QComboBox::activated), timer, [timer]() {
241
        timer->start(100);
242
    });
243

244
    return Application::Instance->commandManager().signalChanged.connect([timer](){
245
        timer->start(100);
246
    });
247
}
248

249
void DlgCustomKeyboardImp::initPriorityList(QTreeWidget *priorityList,
250
                                            QAbstractButton *buttonUp,
251
                                            QAbstractButton *buttonDown)
252
{
253
    QStringList labels;
254
    labels << tr("Name") << tr("Title");
255
    priorityList->setHeaderLabels(labels);
256
    priorityList->header()->hide();
257
    priorityList->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
258
    priorityList->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
259

260
    auto updatePriorityList = [priorityList](bool up) {
261
        auto item = priorityList->currentItem();
262
        if (!item)
263
            return;
264

265
        int index = priorityList->indexOfTopLevelItem(item);
266
        if (index < 0)
267
            return;
268
        if ((index == 0 && up)
269
                || (index == priorityList->topLevelItemCount()-1 && !up))
270
            return;
271

272
        std::vector<QByteArray> actions;
273
        for (int i=0; i<priorityList->topLevelItemCount(); ++i) {
274
            auto item = priorityList->topLevelItem(i);
275
            actions.push_back(item->data(0, Qt::UserRole).toByteArray());
276
        }
277

278
        auto it = actions.begin() + index;
279
        auto itNext = up ? it - 1 : it + 1;
280
        std::swap(*it, *itNext);
281
        ShortcutManager::instance()->setPriorities(actions);
282
    };
283

284
    QObject::connect(buttonUp, &QAbstractButton::clicked, [=](){updatePriorityList(true);});
285
    QObject::connect(buttonDown, &QAbstractButton::clicked, [=](){updatePriorityList(false);});
286
    QObject::connect(priorityList, &QTreeWidget::currentItemChanged,
287
        [=](QTreeWidgetItem *item){
288
            buttonUp->setEnabled(item!=nullptr);
289
            buttonDown->setEnabled(item!=nullptr);
290
        }
291
    );
292
}
293

294
boost::signals2::connection
295
DlgCustomKeyboardImp::initCommandWidgets(QTreeWidget *commandTreeWidget,
296
                                         QTreeWidgetItem *separatorItem,
297
                                         QComboBox *comboGroups,
298
                                         QLineEdit *editCommand,
299
                                         QTreeWidget *priorityList,
300
                                         QAbstractButton *buttonUp,
301
                                         QAbstractButton *buttonDown,
302
                                         Gui::AccelLineEdit *editShortcut,
303
                                         Gui::AccelLineEdit *currentShortcut)
304
{
305
    initCommandCompleter(editCommand, comboGroups, commandTreeWidget, separatorItem);
306
    auto conn = initCommandList(commandTreeWidget, separatorItem, comboGroups);
307

308
    if (priorityList && buttonUp && buttonDown) {
309
        initPriorityList(priorityList, buttonUp, buttonDown);
310

311
        auto timer = new QTimer(priorityList);
312
        timer->setSingleShot(true);
313
        if (currentShortcut) {
314
            QObject::connect(currentShortcut, &QLineEdit::textChanged, timer, [timer]() {
315
                timer->start(200);
316
            });
317
        }
318
        QObject::connect(editShortcut, &QLineEdit::textChanged, timer, [timer]() {
319
            timer->start(200);
320
        });
321
        QObject::connect(ShortcutManager::instance(), &ShortcutManager::priorityChanged, timer, [timer](){
322
            timer->start(200);
323
        });
324
        QObject::connect(timer, &QTimer::timeout, [=]() {
325
            populatePriorityList(priorityList, editShortcut, currentShortcut);
326
        });
327
    }
328

329
    return conn;
330
}
331

332
void DlgCustomKeyboardImp::populatePriorityList(QTreeWidget *priorityList,
333
                                                Gui::AccelLineEdit *editor,
334
                                                Gui::AccelLineEdit *curShortcut)
335
{
336
    QByteArray current;
337
    if (auto currentItem = priorityList->currentItem())
338
        current = currentItem->data(0, Qt::UserRole).toByteArray();
339

340
    priorityList->clear();
341
    QString sc;
342
    if (!editor->isNone() && editor->text().size())
343
        sc = editor->text();
344
    else if (curShortcut && !curShortcut->isNone())
345
        sc = curShortcut->text();
346

347
    auto actionList = ShortcutManager::instance()->getActionsByShortcut(sc);
348
    QTreeWidgetItem *currentItem = nullptr;
349
    for (const auto &info : actionList) {
350
        if (!info.second)
351
            continue;
352
        QTreeWidgetItem* item = new QTreeWidgetItem(priorityList);
353
        item->setText(0, QString::fromUtf8(info.first));
354
        item->setText(1, Action::cleanTitle(info.second->text()));
355
        item->setToolTip(0, info.second->toolTip());
356
        item->setIcon(0, info.second->icon());
357
        item->setData(0, Qt::UserRole, info.first);
358
        if (current == info.first)
359
            currentItem = item;
360
    }
361
    priorityList->resizeColumnToContents(0);
362
    priorityList->resizeColumnToContents(1);
363
    if (currentItem)
364
        priorityList->setCurrentItem(currentItem);
365
}
366

367
void DlgCustomKeyboardImp::populateCommandGroups(QComboBox *combo)
368
{
369
    CommandManager & cCmdMgr = Application::Instance->commandManager();
370
    std::map<std::string,Command*> sCommands = cCmdMgr.getCommands();
371

372
    GroupMap groupMap;
373
    groupMap.push_back(std::make_pair(QLatin1String("File"), QString()));
374
    groupMap.push_back(std::make_pair(QLatin1String("Edit"), QString()));
375
    groupMap.push_back(std::make_pair(QLatin1String("View"), QString()));
376
    groupMap.push_back(std::make_pair(QLatin1String("Standard-View"), QString()));
377
    groupMap.push_back(std::make_pair(QLatin1String("Tools"), QString()));
378
    groupMap.push_back(std::make_pair(QLatin1String("Window"), QString()));
379
    groupMap.push_back(std::make_pair(QLatin1String("Help"), QString()));
380
    groupMap.push_back(std::make_pair(QLatin1String("Macros"), qApp->translate("Gui::MacroCommand", "Macros")));
381

382
    for (const auto & sCommand : sCommands) {
383
        QLatin1String group(sCommand.second->getGroupName());
384
        QString text = sCommand.second->translatedGroupName();
385
        GroupMap::iterator jt;
386
        jt = std::find_if(groupMap.begin(), groupMap.end(), GroupMap_find(group));
387
        if (jt != groupMap.end()) {
388
            if (jt->second.isEmpty())
389
                jt->second = text;
390
        }
391
        else {
392
            groupMap.push_back(std::make_pair(group, text));
393
        }
394
    }
395
    groupMap.push_back(std::make_pair(QLatin1String("All"), tr("All")));
396

397
    for (const auto & it : groupMap) {
398
        if (combo->findData(it.first) < 0) {
399
            combo->addItem(it.second);
400
            combo->setItemData(combo->count()-1, QVariant(it.first), Qt::UserRole);
401
        }
402
    }
403
}
404

405
void DlgCustomKeyboardImp::showEvent(QShowEvent* e)
406
{
407
    Q_UNUSED(e);
408
    // If we did this already in the constructor we wouldn't get the vertical scrollbar if needed.
409
    // The problem was noticed with Qt 4.1.4 but may arise with any later version.
410
    if (firstShow) {
411
        ui->categoryBox->activated(ui->categoryBox->currentIndex());
412
        firstShow = false;
413
    }
414
}
415

416
/** Shows the description for the corresponding command */
417
void DlgCustomKeyboardImp::onCommandTreeWidgetCurrentItemChanged(QTreeWidgetItem* item)
418
{
419
    if (!item)
420
        return;
421

422
    QVariant data = item->data(1, Qt::UserRole);
423
    QByteArray name = data.toByteArray(); // command name
424

425
    CommandManager & cCmdMgr = Application::Instance->commandManager();
426
    Command* cmd = cCmdMgr.getCommandByName(name.constData());
427
    if (cmd) {
428
        QKeySequence ks = ShortcutManager::instance()->getShortcut(
429
                cmd->getName(), cmd->getAccel());
430
        QKeySequence ks2 = QString::fromLatin1(cmd->getAccel());
431
        QKeySequence ks3 = ui->editShortcut->text();
432
        if (ks.isEmpty())
433
            ui->accelLineEditShortcut->setText( tr("none") );
434
        else
435
            ui->accelLineEditShortcut->setText(ks.toString(QKeySequence::NativeText));
436

437
        ui->buttonAssign->setEnabled(!ui->editShortcut->text().isEmpty() && (ks != ks3));
438
        ui->buttonReset->setEnabled((ks != ks2));
439
    }
440
}
441

442
/** Shows all commands of this category */
443
void DlgCustomKeyboardImp::onCategoryBoxActivated(int)
444
{
445
    ui->buttonAssign->setEnabled(false);
446
    ui->buttonReset->setEnabled(false);
447
    ui->accelLineEditShortcut->clear();
448
    ui->editShortcut->clear();
449
}
450

451
void DlgCustomKeyboardImp::setShortcutOfCurrentAction(const QString& accelText)
452
{
453
    QTreeWidgetItem* item = ui->commandTreeWidget->currentItem();
454
    if (!item)
455
        return;
456

457
    QVariant data = item->data(1, Qt::UserRole);
458
    QByteArray name = data.toByteArray(); // command name
459

460
    QString nativeText;
461
    if (!accelText.isEmpty()) {
462
        QKeySequence shortcut = accelText;
463
        nativeText = shortcut.toString(QKeySequence::NativeText);
464
        ui->accelLineEditShortcut->setText(accelText);
465
        ui->editShortcut->clear();
466
    }
467
    else {
468
        ui->accelLineEditShortcut->clear();
469
        ui->editShortcut->clear();
470
    }
471
    ShortcutManager::instance()->setShortcut(name, nativeText.toLatin1());
472

473
    ui->buttonAssign->setEnabled(false);
474
    ui->buttonReset->setEnabled(true);
475
}
476

477
/** Assigns a new accelerator to the selected command. */
478
void DlgCustomKeyboardImp::onButtonAssignClicked()
479
{
480
    setShortcutOfCurrentAction(ui->editShortcut->text());
481
}
482

483
/** Clears the accelerator of the selected command. */
484
void DlgCustomKeyboardImp::onButtonClearClicked()
485
{
486
    setShortcutOfCurrentAction(QString());
487
}
488

489
/** Resets the accelerator of the selected command to the default. */
490
void DlgCustomKeyboardImp::onButtonResetClicked()
491
{
492
    QTreeWidgetItem* item = ui->commandTreeWidget->currentItem();
493
    if (!item)
494
        return;
495

496
    QVariant data = item->data(1, Qt::UserRole);
497
    QByteArray name = data.toByteArray(); // command name
498
    ShortcutManager::instance()->reset(name);
499

500
    QString txt = ShortcutManager::instance()->getShortcut(name);
501
    ui->accelLineEditShortcut->setText((txt.isEmpty() ? tr("none") : txt));
502
    ui->buttonReset->setEnabled( false );
503
}
504

505
/** Resets the accelerator of all commands to the default. */
506
void DlgCustomKeyboardImp::onButtonResetAllClicked()
507
{
508
    ShortcutManager::instance()->resetAll();
509
    ui->buttonReset->setEnabled(false);
510
}
511

512
/** Checks for an already occupied shortcut. */
513
void DlgCustomKeyboardImp::onEditShortcutTextChanged(const QString& )
514
{
515
    QTreeWidgetItem* item = ui->commandTreeWidget->currentItem();
516
    if (item) {
517
        QVariant data = item->data(1, Qt::UserRole);
518
        QByteArray name = data.toByteArray(); // command name
519

520
        CommandManager & cCmdMgr = Application::Instance->commandManager();
521
        Command* cmd = cCmdMgr.getCommandByName(name.constData());
522

523
        if (!ui->editShortcut->isNone())
524
            ui->buttonAssign->setEnabled(true);
525
        else {
526
            if (cmd && cmd->getAction() && cmd->getAction()->shortcut().isEmpty())
527
                ui->buttonAssign->setEnabled(false); // both key sequences are empty
528
        }
529
    }
530
}
531

532
void DlgCustomKeyboardImp::onAddMacroAction(const QByteArray&)
533
{
534
}
535

536
void DlgCustomKeyboardImp::onRemoveMacroAction(const QByteArray&)
537
{
538
}
539

540
void DlgCustomKeyboardImp::onModifyMacroAction(const QByteArray&)
541
{
542
    QVariant data = ui->categoryBox->itemData(ui->categoryBox->currentIndex(), Qt::UserRole);
543
    QString group = data.toString();
544
    if (group == QLatin1String("Macros")) {
545
        ui->categoryBox->activated(ui->categoryBox->currentIndex());
546
    }
547
}
548

549
void DlgCustomKeyboardImp::changeEvent(QEvent *e)
550
{
551
    if (e->type() == QEvent::LanguageChange) {
552
        ui->retranslateUi(this);
553
        int count = ui->categoryBox->count();
554

555
        CommandManager & cCmdMgr = Application::Instance->commandManager();
556
        for (int i=0; i<count; i++) {
557
            QVariant data = ui->categoryBox->itemData(i, Qt::UserRole);
558
            std::vector<Command*> aCmds = cCmdMgr.getGroupCommands(data.toByteArray());
559
            if (!aCmds.empty()) {
560
                QString text = aCmds[0]->translatedGroupName();
561
                ui->categoryBox->setItemText(i, text);
562
            }
563
        }
564
        ui->categoryBox->activated(ui->categoryBox->currentIndex());
565
    }
566
    else if (e->type() == QEvent::StyleChange) {
567
        ui->categoryBox->activated(ui->categoryBox->currentIndex());
568
    }
569

570
    QWidget::changeEvent(e);
571
}
572

573
#include "moc_DlgKeyboardImp.cpp"
574

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.