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 language: cpp
env:
- QT4=1
- QT4=0
os: os:
- linux - linux
- osx - osx
compiler: compiler:
- gcc - gcc
- clang - clang
script: mkdir build && cd build && cmake .. -DWITH_SERVER=1 && make script: ./travis-compile.sh
install: ./travis-dependencies.sh install: ./travis-dependencies.sh
cache: apt 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. 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 # 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: Dependencies:
@ -48,6 +48,11 @@ The following flags can be passed to `cmake`:
`cockatrice` is the game client `cockatrice` is the game client
`servatrice` is the server `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 # License
Cockatrice is free software, licensed under the GPLv2; see COPYING for details. 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/hand.svg</file>
<file>resources/pencil.svg</file> <file>resources/pencil.svg</file>
<file>resources/icon_search.svg</file> <file>resources/icon_search.svg</file>
<file>resources/icon_search_black.svg</file>
<file>resources/icon_clearsearch.svg</file> <file>resources/icon_clearsearch.svg</file>
<file>resources/icon_update.png</file> <file>resources/icon_update.png</file>
<file>resources/icon_view.svg</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 <QCursor>
#include <QGraphicsSceneMouseEvent> #include <QGraphicsSceneMouseEvent>
#include <QDebug> #include <QDebug>
#include <QPainter>
static const float CARD_WIDTH_HALF = CARD_WIDTH / 2; static const float CARD_WIDTH_HALF = CARD_WIDTH / 2;
static const float CARD_HEIGHT_HALF = CARD_HEIGHT / 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) AbstractCardDragItem::AbstractCardDragItem(AbstractCardItem *_item, const QPointF &_hotSpot, AbstractCardDragItem *parentDrag)
: QGraphicsItem(), item(_item), hotSpot(_hotSpot) : QGraphicsItem(), item(_item), hotSpot(_hotSpot)
@ -40,6 +42,9 @@ AbstractCardDragItem::~AbstractCardDragItem()
void AbstractCardDragItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) void AbstractCardDragItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{ {
item->paint(painter, option, 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) void AbstractCardDragItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)

View file

@ -190,6 +190,8 @@ void AbstractCardItem::setHovered(bool _hovered)
processHoverEvent(); processHoverEvent();
isHovered = _hovered; isHovered = _hovered;
setZValue(_hovered ? 2000000004 : realZValue); setZValue(_hovered ? 2000000004 : realZValue);
setScale(_hovered ? 1.1 : 1);
setTransformOriginPoint(_hovered ? CARD_WIDTH / 2 : 0, _hovered ? CARD_HEIGHT / 2 : 0);
update(); update();
} }

View file

@ -227,16 +227,18 @@ QString PictureLoader::getPicUrl()
return picUrl; return picUrl;
} }
// otherwise, fallback to the default url // if a card has a muid, use the default url; if not, use the fallback
picUrl = picDownloadHq ? settingsCache->getPicUrlHq() : settingsCache->getPicUrl(); int muid = set ? card->getMuId(set->getShortName()) : 0;
picUrl.replace("!name!", QUrl::toPercentEncoding(card->getCorrectedName())); 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) { if (set) {
picUrl.replace("!setcode!", QUrl::toPercentEncoding(set->getShortName())); picUrl.replace("!setcode!", QUrl::toPercentEncoding(set->getShortName()));
picUrl.replace("!setname!", QUrl::toPercentEncoding(set->getLongName())); 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!") || if (picUrl.contains("!name!") ||

View file

@ -27,16 +27,17 @@ GameSelector::GameSelector(AbstractClient *_client, const TabSupervisor *_tabSup
gameListProxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); gameListProxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
gameListView->setModel(gameListProxyModel); gameListView->setModel(gameListProxyModel);
gameListView->setSortingEnabled(true); gameListView->setSortingEnabled(true);
gameListView->sortByColumn(gameListModel->startTimeColIndex(), Qt::AscendingOrder);
gameListView->setAlternatingRowColors(true); gameListView->setAlternatingRowColors(true);
gameListView->setRootIsDecorated(true); gameListView->setRootIsDecorated(true);
if (_room) if (_room)
gameListView->header()->hideSection(1); gameListView->header()->hideSection(gameListModel->roomColIndex());
else else
gameListProxyModel->setUnavailableGamesVisible(true); gameListProxyModel->setUnavailableGamesVisible(true);
#if QT_VERSION < 0x050000 #if QT_VERSION < 0x050000
gameListView->header()->setResizeMode(1, QHeaderView::ResizeToContents); gameListView->header()->setResizeMode(0, QHeaderView::ResizeToContents);
#else #else
gameListView->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); gameListView->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
#endif #endif
filterButton = new QPushButton; filterButton = new QPushButton;
filterButton->setIcon(QIcon(":/resources/icon_search.svg")); filterButton->setIcon(QIcon(":/resources/icon_search.svg"));
@ -53,7 +54,7 @@ GameSelector::GameSelector(AbstractClient *_client, const TabSupervisor *_tabSup
createButton = 0; createButton = 0;
joinButton = new QPushButton; joinButton = new QPushButton;
spectateButton = new QPushButton; spectateButton = new QPushButton;
QHBoxLayout *buttonLayout = new QHBoxLayout; QHBoxLayout *buttonLayout = new QHBoxLayout;
buttonLayout->addWidget(filterButton); buttonLayout->addWidget(filterButton);
buttonLayout->addWidget(clearFilterButton); buttonLayout->addWidget(clearFilterButton);
@ -63,7 +64,7 @@ GameSelector::GameSelector(AbstractClient *_client, const TabSupervisor *_tabSup
buttonLayout->addWidget(joinButton); buttonLayout->addWidget(joinButton);
buttonLayout->addWidget(spectateButton); buttonLayout->addWidget(spectateButton);
buttonLayout->setAlignment(Qt::AlignTop); buttonLayout->setAlignment(Qt::AlignTop);
QVBoxLayout *mainLayout = new QVBoxLayout; QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(gameListView); mainLayout->addWidget(gameListView);
mainLayout->addLayout(buttonLayout); mainLayout->addLayout(buttonLayout);
@ -76,6 +77,8 @@ GameSelector::GameSelector(AbstractClient *_client, const TabSupervisor *_tabSup
connect(joinButton, SIGNAL(clicked()), this, SLOT(actJoin())); connect(joinButton, SIGNAL(clicked()), this, SLOT(actJoin()));
connect(spectateButton, 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() void GameSelector::actSetFilter()

View file

@ -1,6 +1,67 @@
#include "gamesmodel.h" #include "gamesmodel.h"
#include "pb/serverinfo_game.pb.h" #include "pb/serverinfo_game.pb.h"
#include <QDebug>
#include <QStringList> #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) GamesModel::GamesModel(const QMap<int, QString> &_rooms, const QMap<int, GameTypeMap> &_gameTypes, QObject *parent)
: QAbstractTableModel(parent), rooms(_rooms), gameTypes(_gameTypes) : QAbstractTableModel(parent), rooms(_rooms), gameTypes(_gameTypes)
@ -13,25 +74,39 @@ QVariant GamesModel::data(const QModelIndex &index, int role) const
return QVariant(); return QVariant();
if (role == Qt::UserRole) if (role == Qt::UserRole)
return index.row(); return index.row();
if (role != Qt::DisplayRole) if (role != Qt::DisplayRole && role != SORT_ROLE)
return QVariant(); return QVariant();
if ((index.row() >= gameList.size()) || (index.column() >= columnCount())) if ((index.row() >= gameList.size()) || (index.column() >= columnCount()))
return QVariant(); return QVariant();
const ServerInfo_Game &g = gameList[index.row()]; const ServerInfo_Game &g = gameList[index.row()];
switch (index.column()) { switch (index.column()) {
case 0: return QString::fromStdString(g.description()); case 0: return rooms.value(g.room_id());
case 1: return rooms.value(g.room_id()); case 1: {
case 2: return QString::fromStdString(g.creator_info().name()); uint32_t now = time(NULL);
case 3: { 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; QStringList result;
GameTypeMap gameTypeMap = gameTypes.value(g.room_id()); GameTypeMap gameTypeMap = gameTypes.value(g.room_id());
for (int i = g.game_types_size() - 1; i >= 0; --i) for (int i = g.game_types_size() - 1; i >= 0; --i)
result.append(gameTypeMap.value(g.game_types(i))); result.append(gameTypeMap.value(g.game_types(i)));
return result.join(", "); 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: return g.with_password() ? ((g.spectators_need_password() || !g.spectators_allowed()) ? tr("yes") : tr("yes, free for spectators")) : tr("no");
case 5: { case 6: {
QStringList result; QStringList result;
if (g.only_buddies()) if (g.only_buddies())
result.append(tr("buddies only")); result.append(tr("buddies only"));
@ -39,8 +114,8 @@ QVariant GamesModel::data(const QModelIndex &index, int role) const
result.append(tr("reg. users only")); result.append(tr("reg. users only"));
return result.join(", "); return result.join(", ");
} }
case 6: return QString("%1/%2").arg(g.player_count()).arg(g.max_players()); case 7: 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 8: return g.spectators_allowed() ? QVariant(g.spectators_count()) : QVariant(tr("not allowed"));
default: return QVariant(); default: return QVariant();
} }
} }
@ -50,14 +125,15 @@ QVariant GamesModel::headerData(int section, Qt::Orientation orientation, int ro
if ((role != Qt::DisplayRole) || (orientation != Qt::Horizontal)) if ((role != Qt::DisplayRole) || (orientation != Qt::Horizontal))
return QVariant(); return QVariant();
switch (section) { switch (section) {
case 0: return tr("Description"); case 0: return tr("Room");
case 1: return tr("Room"); case 1: return tr("Game Created");
case 2: return tr("Creator"); case 2: return tr("Description");
case 3: return tr("Game type"); case 3: return tr("Creator");
case 4: return tr("Password"); case 4: return tr("Game Type");
case 5: return tr("Restrictions"); case 5: return tr("Password");
case 6: return tr("Players"); case 6: return tr("Restrictions");
case 7: return tr("Spectators"); case 7: return tr("Players");
case 8: return tr("Spectators");
default: return QVariant(); default: return QVariant();
} }
} }
@ -70,7 +146,7 @@ const ServerInfo_Game &GamesModel::getGame(int row)
void GamesModel::updateGameList(const ServerInfo_Game &game) 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 (gameList[i].game_id() == game.game_id()) {
if (game.closed()) { if (game.closed()) {
beginRemoveRows(QModelIndex(), i, i); beginRemoveRows(QModelIndex(), i, i);
@ -78,10 +154,11 @@ void GamesModel::updateGameList(const ServerInfo_Game &game)
endRemoveRows(); endRemoveRows();
} else { } else {
gameList[i].MergeFrom(game); gameList[i].MergeFrom(game);
emit dataChanged(index(i, 0), index(i, 7)); emit dataChanged(index(i, 0), index(i, NUM_COLS-1));
} }
return; return;
} }
}
if (game.player_count() <= 0) if (game.player_count() <= 0)
return; return;
beginInsertRows(QModelIndex(), gameList.size(), gameList.size()); beginInsertRows(QModelIndex(), gameList.size(), gameList.size());
@ -97,6 +174,7 @@ GamesProxyModel::GamesProxyModel(QObject *parent, ServerInfo_User *_ownUser)
maxPlayersFilterMin(-1), maxPlayersFilterMin(-1),
maxPlayersFilterMax(-1) maxPlayersFilterMax(-1)
{ {
setSortRole(GamesModel::SORT_ROLE);
setDynamicSortFilter(true); setDynamicSortFilter(true);
} }
@ -146,7 +224,7 @@ void GamesProxyModel::resetFilterParameters()
gameTypeFilter.clear(); gameTypeFilter.clear();
maxPlayersFilterMin = -1; maxPlayersFilterMin = -1;
maxPlayersFilterMax = -1; maxPlayersFilterMax = -1;
invalidateFilter(); invalidateFilter();
} }
@ -155,7 +233,7 @@ bool GamesProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &/*sourc
GamesModel *model = qobject_cast<GamesModel *>(sourceModel()); GamesModel *model = qobject_cast<GamesModel *>(sourceModel());
if (!model) if (!model)
return false; return false;
const ServerInfo_Game &game = model->getGame(sourceRow); const ServerInfo_Game &game = model->getGame(sourceRow);
if (!unavailableGamesVisible) { if (!unavailableGamesVisible) {
if (game.player_count() == game.max_players()) if (game.player_count() == game.max_players())
@ -174,17 +252,17 @@ bool GamesProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &/*sourc
if (!creatorNameFilter.isEmpty()) if (!creatorNameFilter.isEmpty())
if (!QString::fromStdString(game.creator_info().name()).contains(creatorNameFilter, Qt::CaseInsensitive)) if (!QString::fromStdString(game.creator_info().name()).contains(creatorNameFilter, Qt::CaseInsensitive))
return false; return false;
QSet<int> gameTypes; QSet<int> gameTypes;
for (int i = 0; i < game.game_types_size(); ++i) for (int i = 0; i < game.game_types_size(); ++i)
gameTypes.insert(game.game_types(i)); gameTypes.insert(game.game_types(i));
if (!gameTypeFilter.isEmpty() && gameTypes.intersect(gameTypeFilter).isEmpty()) if (!gameTypeFilter.isEmpty() && gameTypes.intersect(gameTypeFilter).isEmpty())
return false; return false;
if ((maxPlayersFilterMin != -1) && ((int)game.max_players() < maxPlayersFilterMin)) if ((maxPlayersFilterMin != -1) && ((int)game.max_players() < maxPlayersFilterMin))
return false; return false;
if ((maxPlayersFilterMax != -1) && ((int)game.max_players() > maxPlayersFilterMax)) if ((maxPlayersFilterMax != -1) && ((int)game.max_players() > maxPlayersFilterMax))
return false; return false;
return true; return true;
} }

View file

@ -16,15 +16,27 @@ private:
QList<ServerInfo_Game> gameList; QList<ServerInfo_Game> gameList;
QMap<int, QString> rooms; QMap<int, QString> rooms;
QMap<int, GameTypeMap> gameTypes; QMap<int, GameTypeMap> gameTypes;
static const int NUM_COLS = 9;
public: public:
static const int SORT_ROLE = Qt::UserRole+1;
GamesModel(const QMap<int, QString> &_rooms, const QMap<int, GameTypeMap> &_gameTypes, QObject *parent = 0); 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 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 data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
const ServerInfo_Game &getGame(int row); const ServerInfo_Game &getGame(int row);
/**
* Update game list with a (possibly new) game.
*/
void updateGameList(const ServerInfo_Game &game); void updateGameList(const ServerInfo_Game &game);
int roomColIndex() { return 0; }
int startTimeColIndex() { return 1; }
const QMap<int, GameTypeMap> &getGameTypes() { return gameTypes; } const QMap<int, GameTypeMap> &getGameTypes() { return gameTypes; }
}; };
@ -39,7 +51,7 @@ private:
int maxPlayersFilterMin, maxPlayersFilterMax; int maxPlayersFilterMin, maxPlayersFilterMax;
public: public:
GamesProxyModel(QObject *parent = 0, ServerInfo_User *_ownUser = 0); GamesProxyModel(QObject *parent = 0, ServerInfo_User *_ownUser = 0);
bool getUnavailableGamesVisible() const { return unavailableGamesVisible; } bool getUnavailableGamesVisible() const { return unavailableGamesVisible; }
void setUnavailableGamesVisible(bool _unavailableGamesVisible); void setUnavailableGamesVisible(bool _unavailableGamesVisible);
bool getPasswordProtectedGamesVisible() const { return passwordProtectedGamesVisible; } 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())); connect(aMoveTopCardsToExile, SIGNAL(triggered()), this, SLOT(actMoveTopCardsToExile()));
aMoveTopCardToBottom = new QAction(this); aMoveTopCardToBottom = new QAction(this);
connect(aMoveTopCardToBottom, SIGNAL(triggered()), this, SLOT(actMoveTopCardToBottom())); connect(aMoveTopCardToBottom, SIGNAL(triggered()), this, SLOT(actMoveTopCardToBottom()));
aMoveBottomCardToGrave = new QAction(this);
connect(aMoveBottomCardToGrave, SIGNAL(triggered()), this, SLOT(actMoveBottomCardToGrave()));
} }
playerMenu = new QMenu(QString()); 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(aMoveTopCardsToGrave);
libraryMenu->addAction(aMoveTopCardsToExile); libraryMenu->addAction(aMoveTopCardsToExile);
libraryMenu->addAction(aMoveTopCardToBottom); libraryMenu->addAction(aMoveTopCardToBottom);
libraryMenu->addAction(aMoveBottomCardToGrave);
deck->setMenu(libraryMenu, aDrawCard); deck->setMenu(libraryMenu, aDrawCard);
} else { } else {
handMenu = 0; handMenu = 0;
@ -493,7 +496,6 @@ void Player::playerListActionTriggered()
if (menu == mRevealLibrary) { if (menu == mRevealLibrary) {
cmd.set_zone_name("deck"); cmd.set_zone_name("deck");
cmd.set_grant_write_access(true);
} else if (menu == mRevealTopCard) { } else if (menu == mRevealTopCard) {
cmd.set_zone_name("deck"); cmd.set_zone_name("deck");
cmd.set_card_id(0); cmd.set_card_id(0);
@ -610,6 +612,7 @@ void Player::retranslateUi()
aMoveTopCardsToGrave->setText(tr("Move top cards to &graveyard...")); aMoveTopCardsToGrave->setText(tr("Move top cards to &graveyard..."));
aMoveTopCardsToExile->setText(tr("Move top cards to &exile...")); aMoveTopCardsToExile->setText(tr("Move top cards to &exile..."));
aMoveTopCardToBottom->setText(tr("Put top card on &bottom")); aMoveTopCardToBottom->setText(tr("Put top card on &bottom"));
aMoveBottomCardToGrave->setText(tr("Put bottom card &in graveyard"));
handMenu->setTitle(tr("&Hand")); handMenu->setTitle(tr("&Hand"));
mRevealHand->setTitle(tr("&Reveal to")); mRevealHand->setTitle(tr("&Reveal to"));
@ -925,6 +928,19 @@ void Player::actMoveTopCardToBottom()
sendGameCommand(cmd); 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() void Player::actUntapAll()
{ {
Command_SetCardAttr cmd; Command_SetCardAttr cmd;

View file

@ -120,6 +120,7 @@ public slots:
void actMoveTopCardsToGrave(); void actMoveTopCardsToGrave();
void actMoveTopCardsToExile(); void actMoveTopCardsToExile();
void actMoveTopCardToBottom(); void actMoveTopCardToBottom();
void actMoveBottomCardToGrave();
void actViewLibrary(); void actViewLibrary();
void actViewTopCards(); void actViewTopCards();
@ -169,7 +170,7 @@ private:
*aViewGraveyard, *aViewRfg, *aViewSideboard, *aViewGraveyard, *aViewRfg, *aViewSideboard,
*aDrawCard, *aDrawCards, *aUndoDraw, *aMulligan, *aShuffle, *aDrawCard, *aDrawCards, *aUndoDraw, *aMulligan, *aShuffle,
*aUntapAll, *aRollDie, *aCreateToken, *aCreateAnotherToken, *aUntapAll, *aRollDie, *aCreateToken, *aCreateAnotherToken,
*aCardMenu; *aCardMenu, *aMoveBottomCardToGrave;
QList<QAction *> aAddCounter, aSetCounter, aRemoveCounter; QList<QAction *> aAddCounter, aSetCounter, aRemoveCounter;
QAction *aPlay, QAction *aPlay,

View file

@ -25,6 +25,8 @@ SettingsCache::SettingsCache()
picDownloadHq = settings->value("personal/picturedownloadhq", false).toBool(); picDownloadHq = settings->value("personal/picturedownloadhq", false).toBool();
picUrl = settings->value("personal/picUrl", PIC_URL_DEFAULT).toString(); picUrl = settings->value("personal/picUrl", PIC_URL_DEFAULT).toString();
picUrlHq = settings->value("personal/picUrlHq", PIC_URL_HQ_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(); mainWindowGeometry = settings->value("interface/main_window_geometry").toByteArray();
notificationsEnabled = settings->value("interface/notificationsenabled", true).toBool(); notificationsEnabled = settings->value("interface/notificationsenabled", true).toBool();
@ -153,6 +155,18 @@ void SettingsCache::setPicUrlHq(const QString &_picUrlHq)
settings->setValue("personal/picUrlHq", 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) void SettingsCache::setNotificationsEnabled(int _notificationsEnabled)
{ {
notificationsEnabled = _notificationsEnabled; notificationsEnabled = _notificationsEnabled;

View file

@ -4,7 +4,9 @@
#include <QObject> #include <QObject>
#define PIC_URL_DEFAULT "http://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=!cardid!&type=card" #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_DEFAULT "http://mtgimage.com/multiverseid/!cardid!.jpg"
#define PIC_URL_HQ_FALLBACK "http://mtgimage.com/set/!setcode!/!name!.jpg"
class QSettings; class QSettings;
@ -57,6 +59,8 @@ private:
bool ignoreUnregisteredUsers; bool ignoreUnregisteredUsers;
QString picUrl; QString picUrl;
QString picUrlHq; QString picUrlHq;
QString picUrlFallback;
QString picUrlHqFallback;
bool attemptAutoConnect; bool attemptAutoConnect;
public: public:
SettingsCache(); SettingsCache();
@ -93,6 +97,8 @@ public:
bool getIgnoreUnregisteredUsers() const { return ignoreUnregisteredUsers; } bool getIgnoreUnregisteredUsers() const { return ignoreUnregisteredUsers; }
QString getPicUrl() const { return picUrl; } QString getPicUrl() const { return picUrl; }
QString getPicUrlHq() const { return picUrlHq; } QString getPicUrlHq() const { return picUrlHq; }
QString getPicUrlFallback() const { return picUrlFallback; }
QString getPicUrlHqFallback() const { return picUrlHqFallback; }
void copyPath(const QString &src, const QString &dst); void copyPath(const QString &src, const QString &dst);
bool getAutoConnect() const { return attemptAutoConnect; } bool getAutoConnect() const { return attemptAutoConnect; }
public slots: public slots:
@ -129,6 +135,8 @@ public slots:
void setIgnoreUnregisteredUsers(bool _ignoreUnregisteredUsers); void setIgnoreUnregisteredUsers(bool _ignoreUnregisteredUsers);
void setPicUrl(const QString &_picUrl); void setPicUrl(const QString &_picUrl);
void setPicUrlHq(const QString &_picUrlHq); void setPicUrlHq(const QString &_picUrlHq);
void setPicUrlFallback(const QString &_picUrlFallback);
void setPicUrlHqFallback(const QString &_picUrlHqFallback);
void setAutoConnect(const bool &_autoConnect); void setAutoConnect(const bool &_autoConnect);
}; };

View file

@ -50,10 +50,12 @@ TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent)
aClearSearch = new QAction(QString(), this); aClearSearch = new QAction(QString(), this);
aClearSearch->setIcon(QIcon(":/resources/icon_clearsearch.svg")); aClearSearch->setIcon(QIcon(":/resources/icon_clearsearch.svg"));
connect(aClearSearch, SIGNAL(triggered()), this, SLOT(actClearSearch())); connect(aClearSearch, SIGNAL(triggered()), this, SLOT(actClearSearch()));
searchLabel = new QLabel();
searchEdit = new SearchLineEdit; 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); setFocusProxy(searchEdit);
setFocusPolicy(Qt::ClickFocus); setFocusPolicy(Qt::ClickFocus);
@ -73,7 +75,6 @@ TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent)
QHBoxLayout *searchLayout = new QHBoxLayout; QHBoxLayout *searchLayout = new QHBoxLayout;
searchLayout->addWidget(deckEditToolBar); searchLayout->addWidget(deckEditToolBar);
searchLayout->addWidget(searchLabel);
searchLayout->addWidget(searchEdit); searchLayout->addWidget(searchEdit);
databaseModel = new CardDatabaseModel(db, this); databaseModel = new CardDatabaseModel(db, this);
@ -293,7 +294,6 @@ void TabDeckEditor::retranslateUi()
{ {
aCardTextOnly->setText(tr("Show card text only")); aCardTextOnly->setText(tr("Show card text only"));
aClearSearch->setText(tr("&Clear search")); aClearSearch->setText(tr("&Clear search"));
searchLabel->setText(tr("&Search for:"));
nameLabel->setText(tr("Deck &name:")); nameLabel->setText(tr("Deck &name:"));
commentsLabel->setText(tr("&Comments:")); commentsLabel->setText(tr("&Comments:"));

View file

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

View file

@ -52,6 +52,8 @@ RoomSelector::RoomSelector(AbstractClient *_client, QWidget *parent)
setLayout(vbox); setLayout(vbox);
connect(client, SIGNAL(listRoomsEventReceived(const Event_ListRooms &)), this, SLOT(processListRoomsEvent(const Event_ListRooms &))); 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())); 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) + " "; callerString = QString::number((qulonglong) caller, 16) + " ";
//filter out all log entries based on values in configuration file //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(); QString logFilters = settingsCache->value("server/logfilters").toString();
QStringList listlogFilters = logFilters.split(",", QString::SkipEmptyParts); QStringList listlogFilters = logFilters.split(",", QString::SkipEmptyParts);
bool shouldWeSkipLine = false; 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 if [[ $TRAVIS_OS_NAME == "osx" ]] ; then
brew update brew update
brew install qt protobuf libgcrypt if (( QT4 )); then
brew install qt protobuf libgcrypt
else
brew install qt5 protobuf libgcrypt
fi
else else
sudo apt-get update -qq if (( QT4 )); then
sudo apt-get install -y qtmobility-dev libprotobuf-dev protobuf-compiler libqt4-dev 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 fi