diff --git a/.travis.yml b/.travis.yml index 74f6feff..a1c468ff 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,26 @@ language: cpp +env: + - QT4=1 + - QT4=0 os: - linux - osx compiler: - gcc - clang -script: mkdir build && cd build && cmake .. -DWITH_SERVER=1 && make +script: ./travis-compile.sh install: ./travis-dependencies.sh cache: apt +notifications: + webhooks: + urls: + - https://webhooks.gitter.im/e/d94969c3b01b22cbdcb7 + on_success: change + on_failure: change + on_start: false +matrix: + fast_finish: true + allow_failures: + - compiler: clang + os: linux + env: QT4=0 diff --git a/README.md b/README.md index f67c6fcc..f66459cd 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,11 @@ a network interface as well. Both client and server are written in Qt, supportin Chat with the Cockatrice developers on Gitter. Come here to talk about the application, features, or just to hang out. For support regarding specific servers, please contact that server's admin or forum for support rather than asking here. -[![Gitter chat](https://badges.gitter.im/Daenyth/Cockatrice.png)](https://gitter.im/Daenyth/Cockatrice) +[![Gitter chat](https://badges.gitter.im/Cockatrice/Cockatrice.png)](https://gitter.im/Cockatrice/Cockatrice) # Building -[![Build Status](https://travis-ci.org/Daenyth/Cockatrice.svg?branch=master)](https://travis-ci.org/Daenyth/Cockatrice) +[![Build Status](https://travis-ci.org/Cockatrice/Cockatrice.svg?branch=master)](https://travis-ci.org/Cockatrice/Cockatrice) Dependencies: @@ -48,6 +48,11 @@ The following flags can be passed to `cmake`: `cockatrice` is the game client `servatrice` is the server +# Community Resources +- [reddit r/Cockatrice](http://reddit.com/r/cockatrice) +- [Woogerworks Server & Forums](http://woogerworks.com) +- [Cockatrice Official Wiki](https://github.com/Cockatrice/Cockatrice/wiki) + # License Cockatrice is free software, licensed under the GPLv2; see COPYING for details. diff --git a/cockatrice/cockatrice.qrc b/cockatrice/cockatrice.qrc index b4d2f05e..34aacb68 100644 --- a/cockatrice/cockatrice.qrc +++ b/cockatrice/cockatrice.qrc @@ -25,6 +25,7 @@ resources/hand.svg resources/pencil.svg resources/icon_search.svg + resources/icon_search_black.svg resources/icon_clearsearch.svg resources/icon_update.png resources/icon_view.svg diff --git a/cockatrice/resources/icon_search_black.svg b/cockatrice/resources/icon_search_black.svg new file mode 100644 index 00000000..05439a12 --- /dev/null +++ b/cockatrice/resources/icon_search_black.svg @@ -0,0 +1,198 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + \ No newline at end of file diff --git a/cockatrice/src/abstractcarddragitem.cpp b/cockatrice/src/abstractcarddragitem.cpp index acc73532..35476941 100644 --- a/cockatrice/src/abstractcarddragitem.cpp +++ b/cockatrice/src/abstractcarddragitem.cpp @@ -3,9 +3,11 @@ #include #include #include +#include static const float CARD_WIDTH_HALF = CARD_WIDTH / 2; static const float CARD_HEIGHT_HALF = CARD_HEIGHT / 2; +const QColor GHOST_MASK = QColor(255, 255, 255, 50); AbstractCardDragItem::AbstractCardDragItem(AbstractCardItem *_item, const QPointF &_hotSpot, AbstractCardDragItem *parentDrag) : QGraphicsItem(), item(_item), hotSpot(_hotSpot) @@ -40,6 +42,9 @@ AbstractCardDragItem::~AbstractCardDragItem() void AbstractCardDragItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { item->paint(painter, option, widget); + + // adds a mask to the card so it looks like the card hasnt been placed yet + painter->fillRect(boundingRect(), GHOST_MASK); } void AbstractCardDragItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) diff --git a/cockatrice/src/abstractcarditem.cpp b/cockatrice/src/abstractcarditem.cpp index 018cc5e0..26359929 100644 --- a/cockatrice/src/abstractcarditem.cpp +++ b/cockatrice/src/abstractcarditem.cpp @@ -190,6 +190,8 @@ void AbstractCardItem::setHovered(bool _hovered) processHoverEvent(); isHovered = _hovered; setZValue(_hovered ? 2000000004 : realZValue); + setScale(_hovered ? 1.1 : 1); + setTransformOriginPoint(_hovered ? CARD_WIDTH / 2 : 0, _hovered ? CARD_HEIGHT / 2 : 0); update(); } diff --git a/cockatrice/src/carddatabase.cpp b/cockatrice/src/carddatabase.cpp index 9fc0295b..2d311900 100644 --- a/cockatrice/src/carddatabase.cpp +++ b/cockatrice/src/carddatabase.cpp @@ -227,16 +227,18 @@ QString PictureLoader::getPicUrl() return picUrl; } - // otherwise, fallback to the default url - picUrl = picDownloadHq ? settingsCache->getPicUrlHq() : settingsCache->getPicUrl(); - picUrl.replace("!name!", QUrl::toPercentEncoding(card->getCorrectedName())); + // if a card has a muid, use the default url; if not, use the fallback + int muid = set ? card->getMuId(set->getShortName()) : 0; + if(muid) + picUrl = picDownloadHq ? settingsCache->getPicUrlHq() : settingsCache->getPicUrl(); + else + picUrl = picDownloadHq ? settingsCache->getPicUrlHqFallback() : settingsCache->getPicUrlFallback(); + picUrl.replace("!name!", QUrl::toPercentEncoding(card->getCorrectedName())); + picUrl.replace("!cardid!", QUrl::toPercentEncoding(QString::number(muid))); if (set) { picUrl.replace("!setcode!", QUrl::toPercentEncoding(set->getShortName())); picUrl.replace("!setname!", QUrl::toPercentEncoding(set->getLongName())); - int muid = card->getMuId(set->getShortName()); - if (muid) - picUrl.replace("!cardid!", QUrl::toPercentEncoding(QString::number(muid))); } if (picUrl.contains("!name!") || diff --git a/cockatrice/src/gameselector.cpp b/cockatrice/src/gameselector.cpp index 25bf2ebd..9b0dc709 100644 --- a/cockatrice/src/gameselector.cpp +++ b/cockatrice/src/gameselector.cpp @@ -27,16 +27,17 @@ GameSelector::GameSelector(AbstractClient *_client, const TabSupervisor *_tabSup gameListProxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); gameListView->setModel(gameListProxyModel); gameListView->setSortingEnabled(true); + gameListView->sortByColumn(gameListModel->startTimeColIndex(), Qt::AscendingOrder); gameListView->setAlternatingRowColors(true); gameListView->setRootIsDecorated(true); if (_room) - gameListView->header()->hideSection(1); + gameListView->header()->hideSection(gameListModel->roomColIndex()); else gameListProxyModel->setUnavailableGamesVisible(true); #if QT_VERSION < 0x050000 - gameListView->header()->setResizeMode(1, QHeaderView::ResizeToContents); + gameListView->header()->setResizeMode(0, QHeaderView::ResizeToContents); #else - gameListView->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); + gameListView->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); #endif filterButton = new QPushButton; filterButton->setIcon(QIcon(":/resources/icon_search.svg")); @@ -53,7 +54,7 @@ GameSelector::GameSelector(AbstractClient *_client, const TabSupervisor *_tabSup createButton = 0; joinButton = new QPushButton; spectateButton = new QPushButton; - + QHBoxLayout *buttonLayout = new QHBoxLayout; buttonLayout->addWidget(filterButton); buttonLayout->addWidget(clearFilterButton); @@ -63,7 +64,7 @@ GameSelector::GameSelector(AbstractClient *_client, const TabSupervisor *_tabSup buttonLayout->addWidget(joinButton); buttonLayout->addWidget(spectateButton); buttonLayout->setAlignment(Qt::AlignTop); - + QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(gameListView); mainLayout->addLayout(buttonLayout); @@ -76,6 +77,8 @@ GameSelector::GameSelector(AbstractClient *_client, const TabSupervisor *_tabSup connect(joinButton, SIGNAL(clicked()), this, SLOT(actJoin())); connect(spectateButton, SIGNAL(clicked()), this, SLOT(actJoin())); + connect(gameListView, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(actJoin())); + connect(gameListView, SIGNAL(activated(const QModelIndex &)), this, SLOT(actJoin())); } void GameSelector::actSetFilter() diff --git a/cockatrice/src/gamesmodel.cpp b/cockatrice/src/gamesmodel.cpp index df0ec880..77e2f7f5 100644 --- a/cockatrice/src/gamesmodel.cpp +++ b/cockatrice/src/gamesmodel.cpp @@ -1,6 +1,67 @@ #include "gamesmodel.h" #include "pb/serverinfo_game.pb.h" +#include #include +#include +#include + +namespace { + const unsigned SECS_PER_MIN = 60; + const unsigned SECS_PER_HOUR = 60 * 60; + + /** + * Pretty print an integer number of seconds ago. Accurate to only one unit, + * rounded; <5 minutes and >5 hours are displayed as such. As a special case, + * time between 60 and 90 minutes will display both the hours and minutes. + * + * For example... + * 0-300 seconds will return "<5m ago" + * 5-59 minutes will return "Xm ago" + * 60-90 minutes will return "Xhr Ym ago" + * 91-300 minutes will return "Xhr ago" + * 300+ minutes will return "5+ hr ago" + */ + QString prettyPrintSecsAgo(uint32_t secs) { + if (secs < SECS_PER_MIN) { + return QObject::tr("<1m ago"); + } + if (secs < SECS_PER_MIN * 5) { + return QObject::tr("<5m ago"); + } + if (secs < SECS_PER_HOUR) { + uint32_t mins = secs / SECS_PER_MIN; + if (secs % SECS_PER_MIN >= 30) + mins++; + //: This will have a number prepended, like "10m ago" + return QString::number(mins).append(QObject::tr("m ago")); + } + // Here, we want to display both the hours and minutes. + // + // There are two small "corner" cases which could be rectified with + // some more knotty iffy-elsey code: + // Between 1:00:00 and 1:00:29 will display "1hr 0m ago" + // Between 1:29:30 and 1:29:59 will display "1hr 31m ago" + // + // Personally, I prefer to keep the code cleaner, and allow these. + if (secs < SECS_PER_MIN * 90) { + uint32_t mins = secs / SECS_PER_MIN - 60; + if (secs % SECS_PER_MIN >= 30) + mins++; + return QObject::tr("1hr ") + .append(QString::number(mins)) + //: This will have a number prepended, like "5m ago" + .append(QObject::tr("m ago")); + } + if (secs < SECS_PER_HOUR * 5) { + uint32_t hours = secs / SECS_PER_HOUR; + if (secs % SECS_PER_HOUR >= SECS_PER_MIN * 30) + hours++; + //: This will have a number prepended, like "2h ago" + return QString::number(hours).append(QObject::tr("hr ago")); + } + return QObject::tr("5+ hrs ago"); + } +} GamesModel::GamesModel(const QMap &_rooms, const QMap &_gameTypes, QObject *parent) : QAbstractTableModel(parent), rooms(_rooms), gameTypes(_gameTypes) @@ -13,25 +74,39 @@ QVariant GamesModel::data(const QModelIndex &index, int role) const return QVariant(); if (role == Qt::UserRole) return index.row(); - if (role != Qt::DisplayRole) + if (role != Qt::DisplayRole && role != SORT_ROLE) return QVariant(); if ((index.row() >= gameList.size()) || (index.column() >= columnCount())) return QVariant(); - + const ServerInfo_Game &g = gameList[index.row()]; switch (index.column()) { - case 0: return QString::fromStdString(g.description()); - case 1: return rooms.value(g.room_id()); - case 2: return QString::fromStdString(g.creator_info().name()); - case 3: { + case 0: return rooms.value(g.room_id()); + case 1: { + uint32_t now = time(NULL); + uint32_t then = g.start_time(); + int secs = now - then; + + switch (role) { + case Qt::DisplayRole: return prettyPrintSecsAgo(secs); + case SORT_ROLE: return QVariant(secs); + default: { + qDebug() << "Returning data for col 1 of games model when role != display, role != sort"; + return QVariant(); // Shouldn't ever be reached. + } + } + } + case 2: return QString::fromStdString(g.description()); + case 3: return QString::fromStdString(g.creator_info().name()); + case 4: { QStringList result; GameTypeMap gameTypeMap = gameTypes.value(g.room_id()); for (int i = g.game_types_size() - 1; i >= 0; --i) result.append(gameTypeMap.value(g.game_types(i))); return result.join(", "); } - case 4: return g.with_password() ? ((g.spectators_need_password() || !g.spectators_allowed()) ? tr("yes") : tr("yes, free for spectators")) : tr("no"); - case 5: { + case 5: return g.with_password() ? ((g.spectators_need_password() || !g.spectators_allowed()) ? tr("yes") : tr("yes, free for spectators")) : tr("no"); + case 6: { QStringList result; if (g.only_buddies()) result.append(tr("buddies only")); @@ -39,8 +114,8 @@ QVariant GamesModel::data(const QModelIndex &index, int role) const result.append(tr("reg. users only")); return result.join(", "); } - case 6: return QString("%1/%2").arg(g.player_count()).arg(g.max_players()); - case 7: return g.spectators_allowed() ? QVariant(g.spectators_count()) : QVariant(tr("not allowed")); + case 7: return QString("%1/%2").arg(g.player_count()).arg(g.max_players()); + case 8: return g.spectators_allowed() ? QVariant(g.spectators_count()) : QVariant(tr("not allowed")); default: return QVariant(); } } @@ -50,14 +125,15 @@ QVariant GamesModel::headerData(int section, Qt::Orientation orientation, int ro if ((role != Qt::DisplayRole) || (orientation != Qt::Horizontal)) return QVariant(); switch (section) { - case 0: return tr("Description"); - case 1: return tr("Room"); - case 2: return tr("Creator"); - case 3: return tr("Game type"); - case 4: return tr("Password"); - case 5: return tr("Restrictions"); - case 6: return tr("Players"); - case 7: return tr("Spectators"); + case 0: return tr("Room"); + case 1: return tr("Game Created"); + case 2: return tr("Description"); + case 3: return tr("Creator"); + case 4: return tr("Game Type"); + case 5: return tr("Password"); + case 6: return tr("Restrictions"); + case 7: return tr("Players"); + case 8: return tr("Spectators"); default: return QVariant(); } } @@ -70,7 +146,7 @@ const ServerInfo_Game &GamesModel::getGame(int row) void GamesModel::updateGameList(const ServerInfo_Game &game) { - for (int i = 0; i < gameList.size(); i++) + for (int i = 0; i < gameList.size(); i++) { if (gameList[i].game_id() == game.game_id()) { if (game.closed()) { beginRemoveRows(QModelIndex(), i, i); @@ -78,10 +154,11 @@ void GamesModel::updateGameList(const ServerInfo_Game &game) endRemoveRows(); } else { gameList[i].MergeFrom(game); - emit dataChanged(index(i, 0), index(i, 7)); + emit dataChanged(index(i, 0), index(i, NUM_COLS-1)); } return; } + } if (game.player_count() <= 0) return; beginInsertRows(QModelIndex(), gameList.size(), gameList.size()); @@ -97,6 +174,7 @@ GamesProxyModel::GamesProxyModel(QObject *parent, ServerInfo_User *_ownUser) maxPlayersFilterMin(-1), maxPlayersFilterMax(-1) { + setSortRole(GamesModel::SORT_ROLE); setDynamicSortFilter(true); } @@ -146,7 +224,7 @@ void GamesProxyModel::resetFilterParameters() gameTypeFilter.clear(); maxPlayersFilterMin = -1; maxPlayersFilterMax = -1; - + invalidateFilter(); } @@ -155,7 +233,7 @@ bool GamesProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &/*sourc GamesModel *model = qobject_cast(sourceModel()); if (!model) return false; - + const ServerInfo_Game &game = model->getGame(sourceRow); if (!unavailableGamesVisible) { if (game.player_count() == game.max_players()) @@ -174,17 +252,17 @@ bool GamesProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &/*sourc if (!creatorNameFilter.isEmpty()) if (!QString::fromStdString(game.creator_info().name()).contains(creatorNameFilter, Qt::CaseInsensitive)) return false; - + QSet gameTypes; for (int i = 0; i < game.game_types_size(); ++i) gameTypes.insert(game.game_types(i)); if (!gameTypeFilter.isEmpty() && gameTypes.intersect(gameTypeFilter).isEmpty()) return false; - + if ((maxPlayersFilterMin != -1) && ((int)game.max_players() < maxPlayersFilterMin)) return false; if ((maxPlayersFilterMax != -1) && ((int)game.max_players() > maxPlayersFilterMax)) return false; - + return true; } diff --git a/cockatrice/src/gamesmodel.h b/cockatrice/src/gamesmodel.h index aeb37e95..85432cf1 100644 --- a/cockatrice/src/gamesmodel.h +++ b/cockatrice/src/gamesmodel.h @@ -16,15 +16,27 @@ private: QList gameList; QMap rooms; QMap gameTypes; + + static const int NUM_COLS = 9; public: + static const int SORT_ROLE = Qt::UserRole+1; + GamesModel(const QMap &_rooms, const QMap &_gameTypes, QObject *parent = 0); int rowCount(const QModelIndex &parent = QModelIndex()) const { return parent.isValid() ? 0 : gameList.size(); } - int columnCount(const QModelIndex &/*parent*/ = QModelIndex()) const { return 8; } + int columnCount(const QModelIndex &/*parent*/ = QModelIndex()) const { return NUM_COLS; } QVariant data(const QModelIndex &index, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; - + const ServerInfo_Game &getGame(int row); + + /** + * Update game list with a (possibly new) game. + */ void updateGameList(const ServerInfo_Game &game); + + int roomColIndex() { return 0; } + int startTimeColIndex() { return 1; } + const QMap &getGameTypes() { return gameTypes; } }; @@ -39,7 +51,7 @@ private: int maxPlayersFilterMin, maxPlayersFilterMax; public: GamesProxyModel(QObject *parent = 0, ServerInfo_User *_ownUser = 0); - + bool getUnavailableGamesVisible() const { return unavailableGamesVisible; } void setUnavailableGamesVisible(bool _unavailableGamesVisible); bool getPasswordProtectedGamesVisible() const { return passwordProtectedGamesVisible; } diff --git a/cockatrice/src/player.cpp b/cockatrice/src/player.cpp index 16b77eae..9c9e7174 100644 --- a/cockatrice/src/player.cpp +++ b/cockatrice/src/player.cpp @@ -236,6 +236,8 @@ Player::Player(const ServerInfo_User &info, int _id, bool _local, TabGame *_pare connect(aMoveTopCardsToExile, SIGNAL(triggered()), this, SLOT(actMoveTopCardsToExile())); aMoveTopCardToBottom = new QAction(this); connect(aMoveTopCardToBottom, SIGNAL(triggered()), this, SLOT(actMoveTopCardToBottom())); + aMoveBottomCardToGrave = new QAction(this); + connect(aMoveBottomCardToGrave, SIGNAL(triggered()), this, SLOT(actMoveBottomCardToGrave())); } playerMenu = new QMenu(QString()); @@ -271,6 +273,7 @@ Player::Player(const ServerInfo_User &info, int _id, bool _local, TabGame *_pare libraryMenu->addAction(aMoveTopCardsToGrave); libraryMenu->addAction(aMoveTopCardsToExile); libraryMenu->addAction(aMoveTopCardToBottom); + libraryMenu->addAction(aMoveBottomCardToGrave); deck->setMenu(libraryMenu, aDrawCard); } else { handMenu = 0; @@ -493,7 +496,6 @@ void Player::playerListActionTriggered() if (menu == mRevealLibrary) { cmd.set_zone_name("deck"); - cmd.set_grant_write_access(true); } else if (menu == mRevealTopCard) { cmd.set_zone_name("deck"); cmd.set_card_id(0); @@ -610,6 +612,7 @@ void Player::retranslateUi() aMoveTopCardsToGrave->setText(tr("Move top cards to &graveyard...")); aMoveTopCardsToExile->setText(tr("Move top cards to &exile...")); aMoveTopCardToBottom->setText(tr("Put top card on &bottom")); + aMoveBottomCardToGrave->setText(tr("Put bottom card &in graveyard")); handMenu->setTitle(tr("&Hand")); mRevealHand->setTitle(tr("&Reveal to")); @@ -925,6 +928,19 @@ void Player::actMoveTopCardToBottom() sendGameCommand(cmd); } +void Player::actMoveBottomCardToGrave() { + CardZone *zone = zones.value("deck"); + Command_MoveCard cmd; + cmd.set_start_zone("deck"); + cmd.mutable_cards_to_move()->add_card()->set_card_id(zone->getCards().size() - 1); + cmd.set_target_player_id(getId()); + cmd.set_target_zone("grave"); + cmd.set_x(0); + cmd.set_y(0); + + sendGameCommand(cmd); +} + void Player::actUntapAll() { Command_SetCardAttr cmd; diff --git a/cockatrice/src/player.h b/cockatrice/src/player.h index d8396e4d..f0227620 100644 --- a/cockatrice/src/player.h +++ b/cockatrice/src/player.h @@ -120,6 +120,7 @@ public slots: void actMoveTopCardsToGrave(); void actMoveTopCardsToExile(); void actMoveTopCardToBottom(); + void actMoveBottomCardToGrave(); void actViewLibrary(); void actViewTopCards(); @@ -169,7 +170,7 @@ private: *aViewGraveyard, *aViewRfg, *aViewSideboard, *aDrawCard, *aDrawCards, *aUndoDraw, *aMulligan, *aShuffle, *aUntapAll, *aRollDie, *aCreateToken, *aCreateAnotherToken, - *aCardMenu; + *aCardMenu, *aMoveBottomCardToGrave; QList aAddCounter, aSetCounter, aRemoveCounter; QAction *aPlay, diff --git a/cockatrice/src/settingscache.cpp b/cockatrice/src/settingscache.cpp index 5821d352..8374bdb5 100644 --- a/cockatrice/src/settingscache.cpp +++ b/cockatrice/src/settingscache.cpp @@ -25,6 +25,8 @@ SettingsCache::SettingsCache() picDownloadHq = settings->value("personal/picturedownloadhq", false).toBool(); picUrl = settings->value("personal/picUrl", PIC_URL_DEFAULT).toString(); picUrlHq = settings->value("personal/picUrlHq", PIC_URL_HQ_DEFAULT).toString(); + picUrlFallback = settings->value("personal/picUrlFallback", PIC_URL_FALLBACK).toString(); + picUrlHqFallback = settings->value("personal/picUrlHqFallback", PIC_URL_HQ_FALLBACK).toString(); mainWindowGeometry = settings->value("interface/main_window_geometry").toByteArray(); notificationsEnabled = settings->value("interface/notificationsenabled", true).toBool(); @@ -153,6 +155,18 @@ void SettingsCache::setPicUrlHq(const QString &_picUrlHq) settings->setValue("personal/picUrlHq", picUrlHq); } +void SettingsCache::setPicUrlFallback(const QString &_picUrlFallback) +{ + picUrlFallback = _picUrlFallback; + settings->setValue("personal/picUrlFallback", picUrlFallback); +} + +void SettingsCache::setPicUrlHqFallback(const QString &_picUrlHqFallback) +{ + picUrlHqFallback = _picUrlHqFallback; + settings->setValue("personal/picUrlHqFallback", picUrlHqFallback); +} + void SettingsCache::setNotificationsEnabled(int _notificationsEnabled) { notificationsEnabled = _notificationsEnabled; diff --git a/cockatrice/src/settingscache.h b/cockatrice/src/settingscache.h index b222a58b..3b1908a9 100644 --- a/cockatrice/src/settingscache.h +++ b/cockatrice/src/settingscache.h @@ -4,7 +4,9 @@ #include #define PIC_URL_DEFAULT "http://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=!cardid!&type=card" +#define PIC_URL_FALLBACK "http://mtgimage.com/set/!setcode!/!name!.jpg" #define PIC_URL_HQ_DEFAULT "http://mtgimage.com/multiverseid/!cardid!.jpg" +#define PIC_URL_HQ_FALLBACK "http://mtgimage.com/set/!setcode!/!name!.jpg" class QSettings; @@ -57,6 +59,8 @@ private: bool ignoreUnregisteredUsers; QString picUrl; QString picUrlHq; + QString picUrlFallback; + QString picUrlHqFallback; bool attemptAutoConnect; public: SettingsCache(); @@ -93,6 +97,8 @@ public: bool getIgnoreUnregisteredUsers() const { return ignoreUnregisteredUsers; } QString getPicUrl() const { return picUrl; } QString getPicUrlHq() const { return picUrlHq; } + QString getPicUrlFallback() const { return picUrlFallback; } + QString getPicUrlHqFallback() const { return picUrlHqFallback; } void copyPath(const QString &src, const QString &dst); bool getAutoConnect() const { return attemptAutoConnect; } public slots: @@ -129,6 +135,8 @@ public slots: void setIgnoreUnregisteredUsers(bool _ignoreUnregisteredUsers); void setPicUrl(const QString &_picUrl); void setPicUrlHq(const QString &_picUrlHq); + void setPicUrlFallback(const QString &_picUrlFallback); + void setPicUrlHqFallback(const QString &_picUrlHqFallback); void setAutoConnect(const bool &_autoConnect); }; diff --git a/cockatrice/src/tab_deck_editor.cpp b/cockatrice/src/tab_deck_editor.cpp index c68b562b..7d9b34d8 100644 --- a/cockatrice/src/tab_deck_editor.cpp +++ b/cockatrice/src/tab_deck_editor.cpp @@ -50,10 +50,12 @@ TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent) aClearSearch = new QAction(QString(), this); aClearSearch->setIcon(QIcon(":/resources/icon_clearsearch.svg")); connect(aClearSearch, SIGNAL(triggered()), this, SLOT(actClearSearch())); - - searchLabel = new QLabel(); searchEdit = new SearchLineEdit; - searchLabel->setBuddy(searchEdit); +#if QT_VERSION >= 0x050300 + searchEdit->addAction(QIcon(":/resources/icon_search_black.svg"), QLineEdit::LeadingPosition); +#endif + searchEdit->setObjectName("searchEdit"); + setFocusProxy(searchEdit); setFocusPolicy(Qt::ClickFocus); @@ -73,7 +75,6 @@ TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent) QHBoxLayout *searchLayout = new QHBoxLayout; searchLayout->addWidget(deckEditToolBar); - searchLayout->addWidget(searchLabel); searchLayout->addWidget(searchEdit); databaseModel = new CardDatabaseModel(db, this); @@ -293,7 +294,6 @@ void TabDeckEditor::retranslateUi() { aCardTextOnly->setText(tr("Show card text only")); aClearSearch->setText(tr("&Clear search")); - searchLabel->setText(tr("&Search for:")); nameLabel->setText(tr("Deck &name:")); commentsLabel->setText(tr("&Comments:")); diff --git a/cockatrice/src/tab_deck_editor.h b/cockatrice/src/tab_deck_editor.h index dd3b56ac..8db00e73 100644 --- a/cockatrice/src/tab_deck_editor.h +++ b/cockatrice/src/tab_deck_editor.h @@ -85,7 +85,6 @@ private: QTreeView *deckView; KeySignals deckViewKeySignals; CardFrame *cardInfo; - QLabel *searchLabel; SearchLineEdit *searchEdit; KeySignals searchKeySignals; diff --git a/cockatrice/src/tab_server.cpp b/cockatrice/src/tab_server.cpp index b82248a3..4fba193b 100644 --- a/cockatrice/src/tab_server.cpp +++ b/cockatrice/src/tab_server.cpp @@ -52,6 +52,8 @@ RoomSelector::RoomSelector(AbstractClient *_client, QWidget *parent) setLayout(vbox); connect(client, SIGNAL(listRoomsEventReceived(const Event_ListRooms &)), this, SLOT(processListRoomsEvent(const Event_ListRooms &))); + connect(roomList, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(joinClicked())); + connect(roomList, SIGNAL(activated(const QModelIndex &)), this, SLOT(joinClicked())); client->sendCommand(client->prepareSessionCommand(Command_ListRooms())); } diff --git a/servatrice/src/server_logger.cpp b/servatrice/src/server_logger.cpp index 1a1e4bed..4896852a 100644 --- a/servatrice/src/server_logger.cpp +++ b/servatrice/src/server_logger.cpp @@ -50,7 +50,7 @@ void ServerLogger::logMessage(QString message, void *caller) callerString = QString::number((qulonglong) caller, 16) + " "; //filter out all log entries based on values in configuration file - bool shouldWeWriteLog = settingsCache->value("server/writelog").toBool(); + bool shouldWeWriteLog = settingsCache->value("server/writelog",1).toBool(); QString logFilters = settingsCache->value("server/logfilters").toString(); QStringList listlogFilters = logFilters.split(",", QString::SkipEmptyParts); bool shouldWeSkipLine = false; diff --git a/travis-compile.sh b/travis-compile.sh new file mode 100755 index 00000000..e5058543 --- /dev/null +++ b/travis-compile.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +set -e + +mkdir build +cd build +prefix="" +if [[ $TRAVIS_OS_NAME == "osx" && $QT4 == 0 ]]; then + prefix="-DCMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.3.2/" +fi +cmake .. -DWITH_SERVER=1 -DWITH_QT4=$QT4 $prefix +make diff --git a/travis-dependencies.sh b/travis-dependencies.sh index 10af11cd..5878b513 100755 --- a/travis-dependencies.sh +++ b/travis-dependencies.sh @@ -2,8 +2,20 @@ if [[ $TRAVIS_OS_NAME == "osx" ]] ; then brew update - brew install qt protobuf libgcrypt + if (( QT4 )); then + brew install qt protobuf libgcrypt + else + brew install qt5 protobuf libgcrypt + fi else - sudo apt-get update -qq - sudo apt-get install -y qtmobility-dev libprotobuf-dev protobuf-compiler libqt4-dev + if (( QT4 )); then + sudo apt-get update -qq + sudo apt-get install -y qtmobility-dev libprotobuf-dev protobuf-compiler libqt4-dev + else + sudo add-apt-repository --yes ppa:ubuntu-sdk-team/ppa + sudo apt-get update -qq + sudo apt-get install -y libprotobuf-dev protobuf-compiler qtbase5-dev cmake\ + qtdeclarative5-dev libqt5webkit5-dev libsqlite3-dev qt5-default qttools5-dev-tools\ + qttools5-dev qtmultimedia5-dev libqt5svg5-dev + fi fi