Merge branch 'master' into set_extinfo

This commit is contained in:
Fabio Bas 2014-11-26 11:45:51 +01:00
commit 2704523c73
20 changed files with 440 additions and 54 deletions

View file

@ -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

View file

@ -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.

View file

@ -25,6 +25,7 @@
<file>resources/hand.svg</file>
<file>resources/pencil.svg</file>
<file>resources/icon_search.svg</file>
<file>resources/icon_search_black.svg</file>
<file>resources/icon_clearsearch.svg</file>
<file>resources/icon_update.png</file>
<file>resources/icon_view.svg</file>

View file

@ -0,0 +1,198 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64"
height="64"
id="svg2"
version="1.1"
inkscape:version="0.48.5 r10040"
sodipodi:docname="icon_search_black.svg">
<defs
id="defs4">
<linearGradient
inkscape:collect="always"
id="linearGradient3709">
<stop
style="stop-color:#4d4d4d;stop-opacity:1;"
offset="0"
id="stop3711" />
<stop
style="stop-color:#4d4d4d;stop-opacity:0;"
offset="1"
id="stop3713" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient3699">
<stop
style="stop-color:#ececec;stop-opacity:1;"
offset="0"
id="stop3701" />
<stop
style="stop-color:#ececec;stop-opacity:0;"
offset="1"
id="stop3703" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient3632">
<stop
style="stop-color:#c87137;stop-opacity:1;"
offset="0"
id="stop3634" />
<stop
style="stop-color:#c87137;stop-opacity:0;"
offset="1"
id="stop3636" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient3622">
<stop
style="stop-color:#f9f9f9;stop-opacity:1;"
offset="0"
id="stop3624" />
<stop
style="stop-color:#f9f9f9;stop-opacity:0;"
offset="1"
id="stop3626" />
</linearGradient>
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 526.18109 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
id="perspective10" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3622"
id="linearGradient3628"
x1="30.59761"
y1="8.9243031"
x2="30.59761"
y2="21.545816"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3632"
id="linearGradient3638"
x1="28.430279"
y1="47.808765"
x2="29.960159"
y2="47.808765"
gradientUnits="userSpaceOnUse" />
<inkscape:perspective
id="perspective3685"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3699"
id="linearGradient3705"
x1="29.215645"
y1="1021.9559"
x2="31.852083"
y2="1021.9559"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3709"
id="linearGradient3715"
x1="22.288248"
y1="35.505978"
x2="49.488647"
y2="49.106178"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3632"
id="linearGradient3730"
gradientUnits="userSpaceOnUse"
x1="28.430279"
y1="47.808765"
x2="29.960159"
y2="47.808765" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3699"
id="linearGradient3732"
gradientUnits="userSpaceOnUse"
x1="29.215645"
y1="1021.9559"
x2="31.852083"
y2="1021.9559" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="7.84375"
inkscape:cx="6.0725518"
inkscape:cy="32"
inkscape:document-units="px"
inkscape:current-layer="g3717"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="1028"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-988.36218)">
<g
id="g3717"
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,729.31175,279.31708)">
<path
transform="matrix(1.7216859,0,0,1.7216859,-37.751505,955.72849)"
d="m 49.338646,35.505978 a 13.450199,13.450199 0 1 1 -26.900398,0 13.450199,13.450199 0 1 1 26.900398,0 z"
sodipodi:ry="13.450199"
sodipodi:rx="13.450199"
sodipodi:cy="35.505978"
sodipodi:cx="35.888447"
id="path2816"
style="fill:#222326;fill-opacity:0;stroke:#222326;stroke-width:1.90745997;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
sodipodi:type="arc" />
<path
transform="matrix(-0.08671958,-1.1164509,1.1164509,-0.08671958,2.9138372,1039.7631)"
d="m 18.613546,52.207172 a 2.5498009,12.302789 0 1 1 -5.099601,0 2.5498009,12.302789 0 1 1 5.099601,0 z"
sodipodi:ry="12.302789"
sodipodi:rx="2.5498009"
sodipodi:cy="52.207172"
sodipodi:cx="16.063745"
id="path2826"
style="fill:#222326;fill-opacity:1;stroke:#222326;stroke-width:0.30000001;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
sodipodi:type="arc" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.1 KiB

View file

@ -3,9 +3,11 @@
#include <QCursor>
#include <QGraphicsSceneMouseEvent>
#include <QDebug>
#include <QPainter>
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)

View file

@ -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();
}

View file

@ -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!") ||

View file

@ -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()

View file

@ -1,6 +1,67 @@
#include "gamesmodel.h"
#include "pb/serverinfo_game.pb.h"
#include <QDebug>
#include <QStringList>
#include <sstream>
#include <time.h>
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<int, QString> &_rooms, const QMap<int, GameTypeMap> &_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<GamesModel *>(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<int> 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;
}

View file

@ -16,15 +16,27 @@ private:
QList<ServerInfo_Game> gameList;
QMap<int, QString> rooms;
QMap<int, GameTypeMap> gameTypes;
static const int NUM_COLS = 9;
public:
static const int SORT_ROLE = Qt::UserRole+1;
GamesModel(const QMap<int, QString> &_rooms, const QMap<int, GameTypeMap> &_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<int, GameTypeMap> &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; }

View file

@ -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;

View file

@ -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<QAction *> aAddCounter, aSetCounter, aRemoveCounter;
QAction *aPlay,

View file

@ -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;

View file

@ -4,7 +4,9 @@
#include <QObject>
#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);
};

View file

@ -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:"));

View file

@ -85,7 +85,6 @@ private:
QTreeView *deckView;
KeySignals deckViewKeySignals;
CardFrame *cardInfo;
QLabel *searchLabel;
SearchLineEdit *searchEdit;
KeySignals searchKeySignals;

View file

@ -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()));
}

View file

@ -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;

12
travis-compile.sh Executable file
View file

@ -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

View file

@ -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