Merge branch 'master' into tooomm-ci_sign_mac

This commit is contained in:
tooomm 2026-06-19 16:55:49 +02:00
commit 31e38011f0
19 changed files with 143 additions and 51 deletions

View file

@ -211,9 +211,10 @@ jobs:
if: github.ref == 'refs/heads/master' && steps.ccache_restore.outputs.cache-hit
continue-on-error: true
env:
CACHE_PRIMARY_KEY: ${{ steps.ccache_restore.outputs.cache-primary-key }}
GH_TOKEN: ${{ github.token }}
run: |
if gh cache delete --repo ${{ github.repository }} ${{ steps.ccache_restore.outputs.cache-primary-key }}; then
if gh cache delete --repo "$GITHUB_REPOSITORY" "$CACHE_PRIMARY_KEY"; then
echo "Cache deleted successfully"
fi
@ -256,8 +257,9 @@ jobs:
if: steps.attestation.outcome == 'success'
shell: bash
env:
BUILD_PATH: ${{ steps.build.outputs.path }}
GH_TOKEN: ${{ github.token }}
run: gh attestation verify "${{ steps.build.outputs.path }}" --repo Cockatrice/Cockatrice
run: gh attestation verify "$BUILD_PATH" --repo Cockatrice/Cockatrice
build-vcpkg:
strategy:
@ -378,9 +380,11 @@ jobs:
# Resolve given wildcard versions (e.g. Qt 6.6.*) to latest version via aqtinstall to avoid stale caches on new releases
- name: "Resolve latest Qt patch version"
env:
QT_VERSION: ${{ matrix.qt_version }}
id: resolve_qt_version
shell: bash
run: .ci/resolve_latest_aqt_qt_version.sh "${{ matrix.qt_version }}"
run: .ci/resolve_latest_aqt_qt_version.sh "$QT_VERSION"
- name: "[macOS] Restore thin Qt ${{ steps.resolve_qt_version.outputs.version }} libraries"
if: matrix.os == 'macOS'
@ -461,9 +465,10 @@ jobs:
if: matrix.os == 'macOS' && matrix.use_ccache == 1 && github.ref == 'refs/heads/master' && steps.ccache_restore.outputs.cache-hit
continue-on-error: true
env:
CACHE_PRIMARY_KEY: ${{ steps.ccache_restore.outputs.cache-primary-key }}
GH_TOKEN: ${{ github.token }}
run: |
if gh cache delete --repo ${{ github.repository }} ${{ steps.ccache_restore.outputs.cache-primary-key }}; then
if gh cache delete --repo "$GITHUB_REPOSITORY" "$CACHE_PRIMARY_KEY"; then
echo "Cache deleted successfully"
fi
@ -478,12 +483,13 @@ jobs:
if: matrix.os == 'macOS' && matrix.make_package && needs.configure.outputs.tag != null
shell: bash
env:
BUILD_PATH: ${{ steps.build.outputs.path }}
MACOS_CERTIFICATE_NAME: ${{ secrets.PROD_MACOS_CERTIFICATE_NAME }}
MACOS_CI_KEYCHAIN_PWD: ${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }}
MACOS_NOTARIZATION_APPLE_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_APPLE_ID }}
MACOS_NOTARIZATION_PWD: ${{ secrets.PROD_MACOS_NOTARIZATION_PWD }}
MACOS_NOTARIZATION_TEAM_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_TEAM_ID }}
run: .ci/sign_macos_bundle.sh "${{ steps.build.outputs.path }}"
run: .ci/sign_macos_bundle.sh "$BUILD_PATH"
- name: "Upload artifact"
if: matrix.make_package
@ -528,5 +534,6 @@ jobs:
if: steps.attestation.outcome == 'success'
shell: bash
env:
BUILD_PATH: ${{ steps.build.outputs.path }}
GH_TOKEN: ${{ github.token }}
run: gh attestation verify "${{ steps.build.outputs.path }}" --repo Cockatrice/Cockatrice
run: gh attestation verify "$BUILD_PATH" --repo Cockatrice/Cockatrice

View file

@ -117,21 +117,22 @@ ${If} $InstDir == ""
; we need to set a default based on the install mode
StrCpy $InstDir $0
${EndIf}
Call SetModeDestinationFromInstdir
; --- Detect portable install when using /R ---
; --- Detect portable install when using /R (must come BEFORE SetModeDestinationFromInstdir) ---
${If} $ReinstallMode = 1
IfFileExists "$InstDir\portable.dat" 0 not_portable
StrCpy $PortableMode 1
Goto portable_done
not_portable:
StrCpy $PortableMode 0
portable_done:
${EndIf}
; Now that $PortableMode reflects reality, commit InstDir into the correct slot
Call SetModeDestinationFromInstdir
${If} $ReinstallMode = 1
${AndIf} $PortableMode = 0
Call AutoUninstallIfNeeded
${EndIf}

View file

@ -309,6 +309,7 @@ SettingsCache::SettingsCache()
cardViewExpandedRowsMax = settings->value("interface/cardViewExpandedRowsMax", 20).toInt();
closeEmptyCardView = settings->value("interface/closeEmptyCardView", true).toBool();
focusCardViewSearchBar = settings->value("interface/focusCardViewSearchBar", true).toBool();
keepGameChatFocus = settings->value("interface/keepGameChatFocus", false).toBool();
showDragSelectionCount = settings->value("interface/showlassoselectioncount", true).toBool();
showTotalSelectionCount = settings->value("interface/showpersistentselectioncount", true).toBool();
@ -457,6 +458,13 @@ void SettingsCache::setFocusCardViewSearchBar(QT_STATE_CHANGED_T value)
settings->setValue("interface/focusCardViewSearchBar", focusCardViewSearchBar);
}
void SettingsCache::setKeepGameChatFocus(QT_STATE_CHANGED_T value)
{
keepGameChatFocus = value;
settings->setValue("interface/keepGameChatFocus", keepGameChatFocus);
emit keepGameChatFocusChanged(keepGameChatFocus);
}
void SettingsCache::setKnownMissingFeatures(const QString &_knownMissingFeatures)
{
knownMissingFeatures = _knownMissingFeatures;

View file

@ -195,6 +195,7 @@ signals:
void downloadSpoilerStatusChanged();
void useTearOffMenusChanged(bool state);
void roundCardCornersChanged(bool roundCardCorners);
void keepGameChatFocusChanged(bool value);
private:
QSettings *settings;
@ -306,6 +307,7 @@ private:
int cardViewExpandedRowsMax;
bool closeEmptyCardView;
bool focusCardViewSearchBar;
bool keepGameChatFocus;
int pixmapCacheSize;
int networkCacheSize;
int redirectCacheTtl;
@ -935,6 +937,7 @@ public:
void setCardViewExpandedRowsMax(int value);
void setCloseEmptyCardView(QT_STATE_CHANGED_T value);
void setFocusCardViewSearchBar(QT_STATE_CHANGED_T value);
void setKeepGameChatFocus(QT_STATE_CHANGED_T value);
QString getClientID() override
{
return clientID;
@ -967,6 +970,10 @@ public:
{
return focusCardViewSearchBar;
}
[[nodiscard]] bool getKeepGameChatFocus() const
{
return keepGameChatFocus;
}
[[nodiscard]] ShortcutsSettings &shortcuts() const
{
return *shortcutsSettings;

View file

@ -223,6 +223,10 @@ private:
{"TabDeckEditor/aLoadDeck", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Load Deck..."),
parseSequenceString("Ctrl+O"),
ShortcutGroup::Deck_Editor)},
{"TabDeckEditor/aLoadDeckFromWebsite",
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Load deck from online service..."),
parseSequenceString("Ctrl+Shift+O"),
ShortcutGroup::Deck_Editor)},
{"TabDeckEditor/aLoadDeckFromClipboard",
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Load Deck from Clipboard..."),
parseSequenceString("Ctrl+Shift+V"),
@ -283,6 +287,10 @@ private:
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Load Deck from Clipboard..."),
parseSequenceString("Ctrl+Shift+V"),
ShortcutGroup::Game_Lobby)},
{"DeckViewContainer/loadFromWebsiteButton",
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Load from website..."),
parseSequenceString("Ctrl+Shift+O"),
ShortcutGroup::Game_Lobby)},
{"DeckViewContainer/unloadDeckButton", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Unload Deck"),
parseSequenceString("Ctrl+Alt+U"),
ShortcutGroup::Game_Lobby)},

View file

@ -882,7 +882,8 @@ void PlayerActions::actCreateToken(TokenInfo tokenToCreate)
ExactCard correctedCard = CardDatabaseManager::query()->guessCard({lastTokenInfo.name, lastTokenInfo.providerId});
if (correctedCard) {
lastTokenInfo.name = correctedCard.getName();
lastTokenTableRow = TableZone::tableRowToGridY(correctedCard.getInfo().getUiAttributes().tableRow);
int tableRow = lastTokenInfo.faceDown ? 2 : correctedCard.getInfo().getUiAttributes().tableRow;
lastTokenTableRow = TableZone::tableRowToGridY(tableRow);
if (lastTokenInfo.pt.isEmpty()) {
lastTokenInfo.pt = correctedCard.getInfo().getPowTough();
}

View file

@ -360,6 +360,16 @@ void DeckViewScene::rebuildTree()
return;
}
QStringList requiredZones = {DECK_ZONE_MAIN, DECK_ZONE_SIDE};
for (const QString &zoneName : requiredZones) {
if (!cardContainers.contains(zoneName)) {
auto *container = new DeckViewCardContainer(zoneName);
cardContainers.insert(zoneName, container);
addItem(container);
}
}
for (auto *currentZone : deck->getZoneNodes()) {
DeckViewCardContainer *container = cardContainers.value(currentZone->getName(), 0);
if (!container) {

View file

@ -209,6 +209,7 @@ void DeckViewContainer::refreshShortcuts()
loadLocalButton->setShortcut(shortcuts.getSingleShortcut("DeckViewContainer/loadLocalButton"));
loadRemoteButton->setShortcut(shortcuts.getSingleShortcut("DeckViewContainer/loadRemoteButton"));
loadFromClipboardButton->setShortcut(shortcuts.getSingleShortcut("DeckViewContainer/loadFromClipboardButton"));
loadFromWebsiteButton->setShortcut(shortcuts.getSingleShortcut("DeckViewContainer/loadFromWebsiteButton"));
unloadDeckButton->setShortcut(shortcuts.getSingleShortcut("DeckViewContainer/unloadDeckButton"));
readyStartButton->setShortcut(shortcuts.getSingleShortcut("DeckViewContainer/readyStartButton"));
sideboardLockButton->setShortcut(shortcuts.getSingleShortcut("DeckViewContainer/sideboardLockButton"));

View file

@ -34,7 +34,6 @@ GameView::GameView(GameScene *scene, QWidget *parent) : QGraphicsView(scene, par
{
setBackgroundBrush(QBrush(QColor(0, 0, 0)));
setRenderHints(QPainter::TextAntialiasing | QPainter::Antialiasing);
setFocusPolicy(Qt::ClickFocus);
setViewportUpdateMode(BoundingRectViewportUpdate);
connect(scene, &GameScene::sceneRectChanged, this, &GameView::updateSceneRect);
@ -44,6 +43,9 @@ GameView::GameView(GameScene *scene, QWidget *parent) : QGraphicsView(scene, par
connect(scene, &GameScene::sigStopRubberBand, this, &GameView::stopRubberBand);
connect(scene, &QGraphicsScene::selectionChanged, this, [this]() { updateTotalSelectionCount(); });
setFocusDisabled(SettingsCache::instance().getKeepGameChatFocus());
connect(&SettingsCache::instance(), &SettingsCache::keepGameChatFocusChanged, this, &GameView::setFocusDisabled);
aCloseMostRecentZoneView = new QAction(this);
connect(aCloseMostRecentZoneView, &QAction::triggered, scene, &GameScene::closeMostRecentZoneView);
@ -186,3 +188,12 @@ void GameView::updateTotalSelectionCount(const QSize &viewSize)
totalCountLabel->hide();
}
}
/**
* Disabling focus on the game view will allow chat to maintain the autofocusing behavior of pre 2.10.3,
* at the cost of disabling the zone view search bar.
*/
void GameView::setFocusDisabled(bool disabled)
{
setFocusPolicy(disabled ? Qt::NoFocus : Qt::ClickFocus);
}

View file

@ -31,6 +31,7 @@ private slots:
void stopRubberBand();
void refreshShortcuts();
void updateTotalSelectionCount(const QSize &viewSize = QSize());
void setFocusDisabled(bool disabled);
public slots:
void updateSceneRect(const QRectF &rect);

View file

@ -75,6 +75,11 @@ ZoneViewWidget::ZoneViewWidget(PlayerLogic *_player,
searchEditProxy->setZValue(ZValues::DRAG_ITEM);
vbox->addItem(searchEditProxy);
// hide search bar if chat autofocus setting is enabled, since typing into it will no longer work anyway
searchEditProxy->setVisible(!SettingsCache::instance().getKeepGameChatFocus());
connect(&SettingsCache::instance(), &SettingsCache::keepGameChatFocusChanged, searchEditProxy,
[searchEditProxy](bool keepFocus) { searchEditProxy->setVisible(!keepFocus); });
// top row
QGraphicsLinearLayout *hTopRow = new QGraphicsLinearLayout(Qt::Horizontal);

View file

@ -219,9 +219,25 @@ void DlgUpdate::downloadError(const QString &errorString)
void DlgUpdate::downloadSuccessful(const QUrl &filepath)
{
setLabel(tr("Installing..."));
QString installerPath = filepath.toLocalFile();
QString appDir = QDir::toNativeSeparators(QCoreApplication::applicationDirPath());
QProcess process;
process.setProgram(installerPath);
// NSIS needs the /D= argument to be an UNQUOTED string, even if it contains spaces. Qt likes to quote arguments if
// they contain spaces, so we use the windows exclusive QProcess::setNativeArguments in the only case where this is
// relevant, which preserves the argument unquoted.
#ifdef Q_OS_WIN
process.setNativeArguments(QString("/R /D=%1").arg(appDir));
#else
// Linux/macOS: normal argument passing (not relevant since they update differently.)
process.setArguments({"/R", QString("/D=%1").arg(appDir)});
#endif
// Try to open the installer. If it opens, quit Cockatrice
if (QProcess::startDetached(
QString("\"%1\" /R /D=\"%2\"").arg(filepath.toLocalFile(), QCoreApplication::applicationDirPath()))) {
if (process.startDetached()) {
QMetaObject::invokeMethod(static_cast<MainWindow *>(parent()), "close", Qt::QueuedConnection);
qCInfo(DlgUpdateLog) << "Opened downloaded update file successfully - closing Cockatrice";
close();

View file

@ -193,6 +193,8 @@ void DeckEditorMenu::refreshShortcuts()
aEditDeckInClipboardRaw->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aEditDeckInClipboardRaw"));
aPrintDeck->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aPrintDeck"));
aLoadDeckFromWebsite->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aLoadDeckFromWebsite"));
aExportDeckDecklist->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aExportDeckDecklist"));
aExportDeckDecklistXyz->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aExportDeckDecklistXyz"));
aAnalyzeDeckDeckstats->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aAnalyzeDeck"));

View file

@ -72,6 +72,10 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage()
connect(&useTearOffMenusCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
[](const QT_STATE_CHANGED_T state) { SettingsCache::instance().setUseTearOffMenus(state == Qt::Checked); });
keepGameChatFocusCheckBox.setChecked(SettingsCache::instance().getKeepGameChatFocus());
connect(&keepGameChatFocusCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setKeepGameChatFocus);
auto *generalGrid = new QGridLayout;
generalGrid->addWidget(&doubleClickToPlayCheckBox, 0, 0);
generalGrid->addWidget(&clickPlaysAllSelectedCheckBox, 1, 0);
@ -83,6 +87,7 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage()
generalGrid->addWidget(&showDragSelectionCountCheckBox, 7, 0);
generalGrid->addWidget(&showTotalSelectionCountCheckBox, 8, 0);
generalGrid->addWidget(&useTearOffMenusCheckBox, 9, 0);
generalGrid->addWidget(&keepGameChatFocusCheckBox, 10, 0);
generalGroupBox = new QGroupBox;
generalGroupBox->setLayout(generalGrid);
@ -207,6 +212,9 @@ void UserInterfaceSettingsPage::retranslateUi()
showDragSelectionCountCheckBox.setText(tr("Show selection counter during drag selection"));
showTotalSelectionCountCheckBox.setText(tr("Show total selection counter"));
useTearOffMenusCheckBox.setText(tr("Use tear-off menus, allowing right click menus to persist on screen"));
keepGameChatFocusCheckBox.setText(
tr("Keep game chat focused when clicking in game (Note: disables card view search bar)"));
notificationsGroupBox->setTitle(tr("Notifications settings"));
notificationsEnabledCheckBox.setText(tr("Enable notifications in taskbar"));
specNotificationsEnabledCheckBox.setText(tr("Notify in the taskbar for game events while you are spectating"));

View file

@ -30,6 +30,7 @@ private:
QCheckBox showDragSelectionCountCheckBox;
QCheckBox showTotalSelectionCountCheckBox;
QCheckBox useTearOffMenusCheckBox;
QCheckBox keepGameChatFocusCheckBox;
QCheckBox tapAnimationCheckBox;
QCheckBox openDeckInNewTabCheckBox;
QLabel visualDeckStoragePromptForConversionLabel;

View file

@ -92,7 +92,7 @@ void CardDatabase::refreshCachedReverseRelatedCards()
}
}
void CardDatabase::addCard(CardInfoPtr card)
void CardDatabase::addCard(const CardInfoPtr &card)
{
if (card == nullptr) {
qCWarning(CardDatabaseLog) << "CardDatabase::addCard(nullptr)";
@ -118,7 +118,7 @@ void CardDatabase::addCard(CardInfoPtr card)
emit cardAdded(card);
}
void CardDatabase::removeCard(CardInfoPtr card)
void CardDatabase::removeCard(const CardInfoPtr &card)
{
if (card.isNull()) {
qCWarning(CardDatabaseLog) << "CardDatabase::removeCard(nullptr)";
@ -143,7 +143,7 @@ void CardDatabase::removeCard(CardInfoPtr card)
emit cardRemoved(card);
}
void CardDatabase::addSet(CardSetPtr set)
void CardDatabase::addSet(const CardSetPtr &set)
{
sets.insert(set->getShortName(), set);
}
@ -215,7 +215,7 @@ void CardDatabase::notifyEnabledSetsChanged()
emit cardDatabaseEnabledSetsChanged();
}
void CardDatabase::addFormat(FormatRulesPtr format)
void CardDatabase::addFormat(const FormatRulesPtr &format)
{
formats.insert(format->formatName.toLower(), format);
}
}

View file

@ -88,7 +88,7 @@ public:
* @brief Removes a card from the database.
* @param card Pointer to the card to remove.
*/
void removeCard(CardInfoPtr card);
void removeCard(const CardInfoPtr &card);
/** @brief Clears all cards, sets, and internal state. */
void clear();
@ -140,15 +140,15 @@ public slots:
* @brief Adds a card to the database.
* @param card CardInfoPtr to add.
*/
void addCard(CardInfoPtr card);
void addCard(const CardInfoPtr &card);
/**
* @brief Adds a set to the database.
* @param set Pointer to CardSet to add.
*/
void addSet(CardSetPtr set);
void addSet(const CardSetPtr &set);
void addFormat(FormatRulesPtr format);
void addFormat(const FormatRulesPtr &format);
/** @brief Loads card databases from configured paths. */
void loadCardDatabases();

View file

@ -217,27 +217,32 @@ void CockatriceXml3Parser::loadCardsFromXml(QXmlStreamReader &xml)
// NOTE: attributes must be read before readElementText()
QXmlStreamAttributes attrs = xml.attributes();
QString setName = xml.readElementText(QXmlStreamReader::IncludeChildElements);
PrintingInfo setInfo(internalAddSet(setName));
if (attrs.hasAttribute("muId")) {
setInfo.setProperty("muid", attrs.value("muId").toString());
}
auto set = internalAddSet(setName);
// Only load printings from sets the user has enabled, matching the v4 loader's
// behaviour. Without this check, disabling a set has no effect on v3 databases.
if (set->getEnabled()) {
PrintingInfo setInfo(set);
if (attrs.hasAttribute("muId")) {
setInfo.setProperty("muid", attrs.value("muId").toString());
}
if (attrs.hasAttribute("muId")) {
setInfo.setProperty("uuid", attrs.value("uuId").toString());
}
if (attrs.hasAttribute("uuId")) {
setInfo.setProperty("uuid", attrs.value("uuId").toString());
}
if (attrs.hasAttribute("picURL")) {
setInfo.setProperty("picurl", attrs.value("picURL").toString());
}
if (attrs.hasAttribute("picURL")) {
setInfo.setProperty("picurl", attrs.value("picURL").toString());
}
if (attrs.hasAttribute("num")) {
setInfo.setProperty("num", attrs.value("num").toString());
}
if (attrs.hasAttribute("num")) {
setInfo.setProperty("num", attrs.value("num").toString());
}
if (attrs.hasAttribute("rarity")) {
setInfo.setProperty("rarity", attrs.value("rarity").toString());
if (attrs.hasAttribute("rarity")) {
setInfo.setProperty("rarity", attrs.value("rarity").toString());
}
_sets[setName].append(setInfo);
}
_sets[setName].append(setInfo);
// related cards
} else if (xmlName == "related" || xmlName == "reverse-related") {
CardRelationType attach = CardRelationType::DoesNotAttach;

View file

@ -63,7 +63,7 @@
<location filename="src/pages.cpp" line="228"/>
<source>Sets file (%1)</source>
<oldsource>Sets JSON file (%1)</oldsource>
<translation type="unfinished"/>
<translation>Archivo de ediciones (%1)</translation>
</message>
<message>
<location filename="src/pages.cpp" line="258"/>
@ -172,7 +172,7 @@
<message>
<location filename="src/pages.cpp" line="726"/>
<source>spoiler</source>
<translation type="unfinished"/>
<translation>spoiler</translation>
</message>
<message>
<location filename="src/pages.cpp" line="731"/>
@ -192,7 +192,7 @@
<message>
<location filename="src/pages.cpp" line="735"/>
<source>Local file:</source>
<translation type="unfinished"/>
<translation>Archivo local:</translation>
</message>
<message>
<location filename="src/pages.cpp" line="736"/>
@ -202,7 +202,7 @@
<message>
<location filename="src/pages.cpp" line="737"/>
<source>Choose file...</source>
<translation type="unfinished"/>
<translation>Elegir archivo...</translation>
</message>
<message>
<location filename="src/pages.cpp" line="739"/>
@ -230,7 +230,7 @@
<message>
<location filename="src/pages.cpp" line="681"/>
<source>tokens</source>
<translation type="unfinished"/>
<translation>fichas</translation>
</message>
<message>
<location filename="src/pages.cpp" line="686"/>
@ -250,7 +250,7 @@
<message>
<location filename="src/pages.cpp" line="690"/>
<source>Local file:</source>
<translation type="unfinished"/>
<translation>Archivo local:</translation>
</message>
<message>
<location filename="src/pages.cpp" line="691"/>
@ -260,7 +260,7 @@
<message>
<location filename="src/pages.cpp" line="692"/>
<source>Choose file...</source>
<translation type="unfinished"/>
<translation>Elegir archivo...</translation>
</message>
<message>
<location filename="src/pages.cpp" line="694"/>
@ -391,12 +391,12 @@
<message>
<location filename="src/pagetemplates.cpp" line="72"/>
<source>Load %1 file</source>
<translation type="unfinished"/>
<translation>Cargar archivo de %1</translation>
</message>
<message>
<location filename="src/pagetemplates.cpp" line="82"/>
<source>%1 file (%1)</source>
<translation type="unfinished"/>
<translation>archivo de %1 (%1)</translation>
</message>
<message>
<location filename="src/pagetemplates.cpp" line="111"/>
@ -420,12 +420,12 @@
<message>
<location filename="src/pagetemplates.cpp" line="129"/>
<source>Please choose a file.</source>
<translation type="unfinished"/>
<translation>Por favor elija un archivo.</translation>
</message>
<message>
<location filename="src/pagetemplates.cpp" line="134"/>
<source>Cannot open file &apos;%1&apos;.</source>
<translation type="unfinished"/>
<translation>No se puede abrir el archivo &apos;%1&apos;.</translation>
</message>
<message>
<location filename="src/pagetemplates.cpp" line="159"/>
@ -602,7 +602,7 @@
<message>
<location filename="src/main.cpp" line="63"/>
<source>Run in no-confirm background mode</source>
<translation type="unfinished"/>
<translation>Ejecutar en modo del segundo plano sin confirmación</translation>
</message>
</context>
</TS>