diff --git a/.travis.yml b/.travis.yml
index 74f6feff6..a1c468ff5 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 f67c6fcc3..f66459cd2 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.
-[](https://gitter.im/Daenyth/Cockatrice)
+[](https://gitter.im/Cockatrice/Cockatrice)
# Building
-[](https://travis-ci.org/Daenyth/Cockatrice)
+[](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 b4d2f05ed..34aacb684 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 000000000..05439a12e
--- /dev/null
+++ b/cockatrice/resources/icon_search_black.svg
@@ -0,0 +1,198 @@
+
+
+
+
\ No newline at end of file
diff --git a/cockatrice/src/abstractcarddragitem.cpp b/cockatrice/src/abstractcarddragitem.cpp
index acc735328..35476941c 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 018cc5e06..263599290 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 9fc0295b8..2d311900e 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 25bf2ebd0..9b0dc709f 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 df0ec8809..77e2f7f52 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 aeb37e95e..85432cf17 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 16b77eae5..9c9e7174b 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 d8396e4d0..f02276200 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 5821d3526..8374bdb58 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 b222a58bc..3b1908a9c 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 c68b562b1..7d9b34d83 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 dd3b56ac9..8db00e739 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 b82248a34..4fba193b3 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 1a1e4bed4..4896852a8 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 000000000..e50585434
--- /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 10af11cd0..5878b513b 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