~ubuntu-branches/ubuntu/wily/hedgewars/wily

« back to all changes in this revision

Viewing changes to QTfrontend/chatwidget.cpp

  • Committer: Package Import Robot
  • Author(s): Dmitry E. Oboukhov
  • Date: 2011-09-23 10:16:55 UTC
  • mfrom: (1.2.11 upstream)
  • Revision ID: package-import@ubuntu.com-20110923101655-3977th2gc5n0a3pv
Tags: 0.9.16-1
* New upstream version.
 + Downloadable content! Simply click to install any content.
   New voices, hats, maps, themes, translations, music, scripts...
   Hedgewars is now more customisable than ever before! As time goes
   by we will be soliciting community content to feature on this page,
   so remember to check it from time to time. If you decide you want
   to go back to standard Hedgewars, just remove the Data directory
   from your Hedgewars config directory.
 + 3-D rendering! Diorama-like rendering of the game in a variety
   of 3D modes. Let us know which ones work best for you, we didn't
   really have the equipment to test them all.
 + Resizable game window.
 + New utilities! The Time Box will remove one of your hedgehogs
   from the game for a while, protecting from attack until it returns,
   somewhere else on the map. Land spray will allow you to build bridges,
   seal up holes, or just make life unpleasant for your enemies.
 + New single player: Bamboo Thicket, That Sinking Feeling, Newton and
   the Tree and multi-player: The Specialists, Space Invaders,
   Racer - scripts! And a ton more script hooks for scripters
 + New twists on old weapons. Drill strike, seduction and fire have
   been adjusted. Defective mines have been added, rope can attach to
   hogs/crates/barrels again, grenades now have variable bounce (use
   precise key + 1-5). Portal gun is now more usable in flight and
   all game actions are a lot faster.
 + New theme - Golf, dozens of new community hats and a new
   localised Default voice, Ukranian.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*
2
2
 * Hedgewars, a free turn based strategy game
3
3
 * Copyright (c) 2007 Igor Ulyanov <iulyanov@gmail.com>
4
 
 * Copyright (c) 2010 Andrey Korotaev <unC0Rr@gmail.com>
 
4
 * Copyright (c) 2007-2011 Andrey Korotaev <unC0Rr@gmail.com>
5
5
 *
6
6
 * This program is free software; you can redistribute it and/or modify
7
7
 * it under the terms of the GNU General Public License as published by
17
17
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18
18
 */
19
19
 
 
20
#include <QDesktopServices>
20
21
#include <QTextBrowser>
21
22
#include <QLineEdit>
22
23
#include <QAction>
23
 
#include <QApplication>
24
24
#include <QTextDocument>
25
25
#include <QDir>
26
26
#include <QSettings>
27
27
#include <QFile>
28
28
#include <QTextStream>
 
29
#include <QMenu>
 
30
#include <QCursor>
 
31
#include <QScrollBar>
 
32
#include <QItemSelectionModel>
29
33
 
30
34
#include "hwconsts.h"
31
35
#include "SDLs.h"
32
36
#include "gameuiconfig.h"
33
37
#include "chatwidget.h"
34
38
 
 
39
ListWidgetNickItem::ListWidgetNickItem(const QString& nick, bool isFriend, bool isIgnored) : QListWidgetItem(nick)
 
40
{
 
41
    this->aFriend = isFriend;
 
42
    this->isIgnored = isIgnored;
 
43
}
 
44
 
 
45
void ListWidgetNickItem::setFriend(bool isFriend)
 
46
{
 
47
    this->aFriend = isFriend;
 
48
}
 
49
 
 
50
void ListWidgetNickItem::setIgnored(bool isIgnored)
 
51
{
 
52
    this->isIgnored = isIgnored;
 
53
}
 
54
 
 
55
bool ListWidgetNickItem::isFriend()
 
56
{
 
57
    return aFriend;
 
58
}
 
59
 
 
60
bool ListWidgetNickItem::ignored()
 
61
{
 
62
    return isIgnored;
 
63
}
 
64
 
 
65
bool ListWidgetNickItem::operator< (const QListWidgetItem & other) const
 
66
{
 
67
    // case in-sensitive comparison of the associated strings
 
68
    // chars that are no letters are sorted at the end of the list
 
69
 
 
70
    ListWidgetNickItem otherNick = const_cast<ListWidgetNickItem &>(dynamic_cast<const ListWidgetNickItem &>(other));
 
71
 
 
72
    // ignored always down
 
73
    if (isIgnored != otherNick.ignored())
 
74
        return !isIgnored;
 
75
 
 
76
    // friends always up
 
77
    if (aFriend != otherNick.isFriend())
 
78
        return aFriend;
 
79
 
 
80
    QString txt1 = text().toLower();
 
81
    QString txt2 = other.text().toLower();
 
82
 
 
83
    bool firstIsShorter = (txt1.size() < txt2.size());
 
84
    int len = firstIsShorter?txt1.size():txt2.size();
 
85
 
 
86
    for (int i = 0; i < len; i++)
 
87
    {
 
88
        if (txt1[i] == txt2[i])
 
89
            continue;
 
90
        if (txt1[i].isLetter() != txt2[i].isLetter())
 
91
            return txt1[i].isLetter();
 
92
        return (txt1[i] < txt2[i]);
 
93
    }
 
94
 
 
95
    return firstIsShorter;
 
96
}
 
97
 
 
98
const char* HWChatWidget::STYLE = 
 
99
"\
 
100
a { color:#c8c8ff; }\
 
101
.nick { text-decoration: none; }\
 
102
.UserChat .nick { color:#ffec20; }\
 
103
.FriendChat { color: #08e008; }\
 
104
.FriendChat .nick { color: #20ff20; }\
 
105
.UserJoin { color: #c0c0c0; }\
 
106
.UserJoin .nick { color: #d0d0d0; }\
 
107
.FriendJoin { color: #c0e0c0; }\
 
108
.FriendJoin .nick { color: #d0f0d0; }\
 
109
.UserAction { color: #ff80ff; }\
 
110
.UserAction .nick { color: #ffa0ff; }\
 
111
.FriendAction { color: #ff00ff; }\
 
112
.FriendAction .nick { color: #ff30ff; }\
 
113
";
 
114
 
35
115
HWChatWidget::HWChatWidget(QWidget* parent, QSettings * gameSettings, SDLInteraction * sdli, bool notify) :
36
116
  QWidget(parent),
37
117
  mainLayout(this)
40
120
    this->sdli = sdli;
41
121
    this->notify = notify;
42
122
    if(notify && gameSettings->value("frontend/sound", true).toBool()) {
43
 
       QDir tmpdir;
44
 
 
45
 
       tmpdir.cd(datadir->absolutePath());
46
 
       tmpdir.cd("Sounds/voices");
47
 
       sdli->SDLMusicInit();
48
 
       sound[0] = Mix_LoadWAV(QString(tmpdir.absolutePath() + "/Classic/Hello.ogg").toLocal8Bit().constData());
49
 
       sound[1] = Mix_LoadWAV(QString(tmpdir.absolutePath() + "/Default/Hello.ogg").toLocal8Bit().constData());
50
 
       sound[2] = Mix_LoadWAV(QString(tmpdir.absolutePath() + "/Mobster/Hello.ogg").toLocal8Bit().constData());
51
 
       sound[3] = Mix_LoadWAV(QString(tmpdir.absolutePath() + "/Russian/Hello.ogg").toLocal8Bit().constData());
 
123
        QFile tmpfile;
 
124
        sdli->SDLMusicInit();
 
125
        for(int i=0;i<4;i++) {
 
126
            tmpfile.setFileName(cfgdir->absolutePath() + "/Data/Sounds/voices/Classic/Hello.ogg");
 
127
            if (tmpfile.exists()) sound[i] = Mix_LoadWAV(QFileInfo(tmpfile).absoluteFilePath().toLocal8Bit().constData());
 
128
            else sound[i] = Mix_LoadWAV(QString(datadir->absolutePath() + 
 
129
                "/Sounds/voices/Classic/Hello.ogg").toLocal8Bit().constData());
 
130
        }
52
131
    }
53
132
 
54
133
    mainLayout.setSpacing(1);
61
140
    chatEditLine->setMaxLength(300);
62
141
    connect(chatEditLine, SIGNAL(returnPressed()), this, SLOT(returnPressed()));
63
142
 
64
 
    mainLayout.addWidget(chatEditLine, 1, 0);
 
143
    mainLayout.addWidget(chatEditLine, 2, 0);
65
144
 
66
145
    chatText = new QTextBrowser(this);
 
146
    chatText->document()->setDefaultStyleSheet(STYLE);
67
147
    chatText->setMinimumHeight(20);
68
148
    chatText->setMinimumWidth(10);
69
149
    chatText->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
70
 
    chatText->setOpenExternalLinks(true);
71
 
    mainLayout.addWidget(chatText, 0, 0);
 
150
    chatText->setOpenLinks(false);
 
151
    connect(chatText, SIGNAL(anchorClicked(const QUrl&)),
 
152
        this, SLOT(linkClicked(const QUrl&)));
 
153
    mainLayout.addWidget(chatText, 0, 0, 2, 1);
72
154
 
73
155
    chatNicks = new QListWidget(this);
74
156
    chatNicks->setMinimumHeight(10);
81
163
    connect(chatNicks, SIGNAL(currentRowChanged(int)),
82
164
        this, SLOT(chatNickSelected(int)));
83
165
 
84
 
    mainLayout.addWidget(chatNicks, 0, 1, -1, 1);
 
166
    mainLayout.addWidget(chatNicks, 0, 1, 3, 1);
85
167
 
86
168
    acInfo = new QAction(QAction::tr("Info"), chatNicks);
87
169
    acInfo->setIcon(QIcon(":/res/info.png"));
102
184
    acFriend->setIcon(QIcon(":/res/addfriend.png"));
103
185
    connect(acFriend, SIGNAL(triggered(bool)), this, SLOT(onFriend()));
104
186
 
 
187
    chatNicks->insertAction(0, acFriend);
105
188
    chatNicks->insertAction(0, acInfo);
106
 
    chatNicks->insertAction(0, acFollow);
107
189
    chatNicks->insertAction(0, acIgnore);
108
 
    chatNicks->insertAction(0, acFriend);
109
190
 
110
191
    showReady = false;
 
192
    setShowFollow(true);
 
193
}
 
194
 
 
195
void HWChatWidget::linkClicked(const QUrl & link)
 
196
{
 
197
    if (link.scheme() == "http")
 
198
        QDesktopServices::openUrl(link);
 
199
    if (link.scheme() == "hwnick")
 
200
    {
 
201
        // decode nick
 
202
        const QString& nick = QString::fromUtf8(QByteArray::fromBase64(link.encodedQuery()));
 
203
        QList<QListWidgetItem *> items = chatNicks->findItems(nick, Qt::MatchExactly);
 
204
        if (items.size() < 1)
 
205
            return;
 
206
        QMenu * popup = new QMenu(this);
 
207
        // selecting an item will automatically scroll there, so let's save old position
 
208
        QScrollBar * scrollBar = chatNicks->verticalScrollBar();
 
209
        int oldScrollPos = scrollBar->sliderPosition();
 
210
        // select the nick which we want to see the actions for
 
211
        chatNicks->setCurrentItem(items[0], QItemSelectionModel::Clear);
 
212
        // selecting an item will automatically scroll there, so let's save old position
 
213
        scrollBar->setSliderPosition(oldScrollPos);
 
214
        // load actions
 
215
        popup->addActions(chatNicks->actions());
 
216
        // display menu popup at mouse cursor position
 
217
        popup->popup(QCursor::pos());
 
218
    }
 
219
}
 
220
 
 
221
void HWChatWidget::setShowFollow(bool enabled)
 
222
{
 
223
    if (enabled) {
 
224
        if (!(chatNicks->actions().contains(acFollow)))
 
225
            chatNicks->insertAction(acFriend, acFollow);
 
226
    }
 
227
    else {
 
228
        if (chatNicks->actions().contains(acFollow))
 
229
            chatNicks->removeAction(acFollow);
 
230
    }
111
231
}
112
232
 
113
233
void HWChatWidget::loadList(QStringList & list, const QString & file)
114
234
{
115
235
    list.clear();
116
 
    QFile txt((cfgdir->absolutePath() + "/" + file).toLocal8Bit().constData());
 
236
    QFile txt(cfgdir->absolutePath() + "/" + file);
117
237
    if(!txt.open(QIODevice::ReadOnly))
118
238
        return;
119
239
    QTextStream stream(&txt);
133
253
 
134
254
void HWChatWidget::saveList(QStringList & list, const QString & file)
135
255
{
136
 
    QFile txt((cfgdir->absolutePath() + "/" + file).toLocal8Bit().constData());
 
256
    QFile txt(cfgdir->absolutePath() + "/" + file);
137
257
    if(!txt.open(QIODevice::WriteOnly | QIODevice::Truncate))
138
258
        return;
139
259
    QTextStream stream(&txt);
145
265
    txt.close();
146
266
}
147
267
 
148
 
void HWChatWidget::updateIcon(QListWidgetItem *item)
 
268
void HWChatWidget::updateNickItem(QListWidgetItem *nickItem)
149
269
{
150
 
    QString nick = item->text();
151
 
 
152
 
    if(ignoreList.contains(nick, Qt::CaseInsensitive))
 
270
    QString nick = nickItem->text();
 
271
    ListWidgetNickItem * item = dynamic_cast<ListWidgetNickItem*>(nickItem);
 
272
 
 
273
    item->setFriend(friendsList.contains(nick, Qt::CaseInsensitive));
 
274
    item->setIgnored(ignoreList.contains(nick, Qt::CaseInsensitive));
 
275
 
 
276
    if(item->ignored())
153
277
    {
154
278
        item->setIcon(QIcon(showReady ? (item->data(Qt::UserRole).toBool() ? ":/res/chat_ignore_on.png" : ":/res/chat_ignore_off.png") : ":/res/chat_ignore.png"));
155
279
        item->setForeground(Qt::gray);
156
280
    }
157
 
    else if(friendsList.contains(nick, Qt::CaseInsensitive))
 
281
    else if(item->isFriend())
158
282
    {
159
283
        item->setIcon(QIcon(showReady ? (item->data(Qt::UserRole).toBool() ? ":/res/chat_friend_on.png" : ":/res/chat_friend_off.png") : ":/res/chat_friend.png"));
160
284
        item->setForeground(Qt::green);
166
290
    }
167
291
}
168
292
 
169
 
void HWChatWidget::updateIcons()
 
293
void HWChatWidget::updateNickItems()
170
294
{
171
295
    for(int i = 0; i < chatNicks->count(); i++)
172
 
        updateIcon(chatNicks->item(i));
 
296
        updateNickItem(chatNicks->item(i));
 
297
 
 
298
    chatNicks->sortItems();
173
299
}
174
300
 
175
301
void HWChatWidget::loadLists(const QString & nick)
176
302
{
177
303
    loadList(ignoreList, nick.toLower() + "_ignore.txt");
178
304
    loadList(friendsList, nick.toLower() + "_friends.txt");
179
 
    updateIcons();
 
305
    updateNickItems();
180
306
}
181
307
 
182
308
void HWChatWidget::saveLists(const QString & nick)
191
317
    chatEditLine->clear();
192
318
}
193
319
 
 
320
 
194
321
void HWChatWidget::onChatString(const QString& str)
195
322
{
 
323
    onChatString("", str);
 
324
}
 
325
 
 
326
const QRegExp HWChatWidget::URLREGEXP = QRegExp("(http://)?(www\\.)?(hedgewars\\.org(/[^ ]*)?)");
 
327
 
 
328
void HWChatWidget::onChatString(const QString& nick, const QString& str)
 
329
{
 
330
    bool isFriend = false;
 
331
 
 
332
    if (!nick.isEmpty()) {
 
333
        // don't show chat lines that are from ignored nicks
 
334
        if (ignoreList.contains(nick, Qt::CaseInsensitive))
 
335
            return;
 
336
        // friends will get special treatment, of course
 
337
        isFriend = friendsList.contains(nick, Qt::CaseInsensitive);
 
338
    }
 
339
 
196
340
    if (chatStrings.size() > 250)
197
341
        chatStrings.removeFirst();
198
342
 
199
343
    QString formattedStr = Qt::escape(str.mid(1));
200
 
    QStringList parts = formattedStr.split(QRegExp("\\W+"), QString::SkipEmptyParts);
201
 
 
202
 
    if (!formattedStr.startsWith(" ***")) // don't ignore status messages
203
 
    {
204
 
        if (formattedStr.startsWith(" *")) // emote
205
 
            parts[0] = parts[1];
206
 
        if(parts.size() > 0 && ignoreList.contains(parts[0], Qt::CaseInsensitive))
207
 
            return;
 
344
    // make hedgewars.org urls actual links
 
345
    formattedStr = formattedStr.replace(URLREGEXP, "<a href=\"http://\\3\">\\3</a>");
 
346
 
 
347
    // "link" nick, but before that encode it in base64 to make sure it can't intefere with html/url syntax
 
348
    // the nick is put as querystring as putting it as host would convert it to it's lower case variant
 
349
    if(!nick.isEmpty())
 
350
        formattedStr.replace("|nick|",QString("<a href=\"hwnick://?%1\" class=\"nick\">%2</a>").arg(QString(nick.toUtf8().toBase64())).arg(nick));
 
351
 
 
352
    QString cssClass("UserChat");
 
353
 
 
354
    // check first character for color code and set color properly
 
355
    switch (str[0].toAscii()) {
 
356
        case 3:
 
357
            cssClass = (isFriend ? "FriendJoin" : "UserJoin");
 
358
            break;
 
359
        case 2:
 
360
            cssClass = (isFriend ? "FriendAction" : "UserAction");
 
361
            break;
 
362
        default:
 
363
            if (isFriend)
 
364
                cssClass = "FriendChat";
208
365
    }
209
366
 
210
 
    QString color("");
211
 
    bool isFriend = friendsList.contains(parts[0], Qt::CaseInsensitive);
212
 
 
213
 
    if (str.startsWith("\x03"))
214
 
        color = QString("#c0c0c0");
215
 
    else if (str.startsWith("\x02"))
216
 
        color = QString(isFriend ? "#00ff00" : "#ff00ff");
217
 
    else if (isFriend)
218
 
        color = QString("#00c000");
219
 
 
220
 
    if(color.compare("") != 0)
221
 
        formattedStr = QString("<font color=\"%2\">%1</font>").arg(formattedStr).arg(color);
 
367
    formattedStr = QString("<span class=\"%2\">%1</span>").arg(formattedStr).arg(cssClass);
222
368
 
223
369
    chatStrings.append(formattedStr);
224
370
 
241
387
 
242
388
void HWChatWidget::nickAdded(const QString& nick, bool notifyNick)
243
389
{
244
 
    QListWidgetItem * item = new QListWidgetItem(nick);
245
 
    updateIcon(item);
 
390
    QListWidgetItem * item = new ListWidgetNickItem(nick, friendsList.contains(nick, Qt::CaseInsensitive), ignoreList.contains(nick, Qt::CaseInsensitive));
 
391
    updateNickItem(item);
246
392
    chatNicks->addItem(item);
247
393
 
 
394
    emit nickCountUpdate(chatNicks->count());
 
395
 
248
396
    if(notifyNick && notify && gameSettings->value("frontend/sound", true).toBool()) {
249
397
       Mix_PlayChannel(-1, sound[rand()%4], 0);
250
398
    }
252
400
 
253
401
void HWChatWidget::nickRemoved(const QString& nick)
254
402
{
255
 
    QList<QListWidgetItem *> items = chatNicks->findItems(nick, Qt::MatchExactly);
256
 
    for(QList<QListWidgetItem *>::iterator it=items.begin(); it!=items.end();) {
257
 
        chatNicks->takeItem(chatNicks->row(*it));
258
 
        ++it;
259
 
    }
 
403
    foreach(QListWidgetItem * item, chatNicks->findItems(nick, Qt::MatchExactly))
 
404
        chatNicks->takeItem(chatNicks->row(item));
 
405
 
 
406
    emit nickCountUpdate(chatNicks->count());
260
407
}
261
408
 
262
409
void HWChatWidget::clear()
307
454
    }
308
455
    else // not on list - add
309
456
    {
 
457
        // don't consider ignored people friends
 
458
        if(friendsList.contains(curritem->text(), Qt::CaseInsensitive))
 
459
            emit onFriend();
 
460
 
 
461
        // scroll down on first ignore added so that people see where that nick went to
 
462
        if (ignoreList.isEmpty())
 
463
            chatNicks->scrollToBottom();
 
464
 
310
465
        ignoreList << curritem->text().toLower();
311
466
        onChatString(HWChatWidget::tr("%1 *** %2 has been added to your ignore list").arg('\x03').arg(curritem->text()));
312
467
    }
313
 
    updateIcon(curritem); // update icon
 
468
    updateNickItem(curritem); // update icon/sort order/etc
 
469
    chatNicks->sortItems();
314
470
    chatNickSelected(0); // update context menu
315
471
}
316
472
 
327
483
    }
328
484
    else // not on list - add
329
485
    {
 
486
        // don't ignore the new friend
 
487
        if(ignoreList.contains(curritem->text(), Qt::CaseInsensitive))
 
488
            emit onIgnore();
 
489
 
 
490
        // scroll up on first friend added so that people see where that nick went to
 
491
        if (friendsList.isEmpty())
 
492
            chatNicks->scrollToTop();
 
493
 
330
494
        friendsList << curritem->text().toLower();
331
495
        onChatString(HWChatWidget::tr("%1 *** %2 has been added to your friends list").arg('\x03').arg(curritem->text()));
332
496
    }
333
 
    updateIcon(curritem); // update icon
 
497
    updateNickItem(curritem); // update icon/sort order/etc
 
498
    chatNicks->sortItems();
334
499
    chatNickSelected(0); // update context menu
335
500
}
336
501
 
337
502
void HWChatWidget::chatNickDoubleClicked(QListWidgetItem * item)
338
503
{
339
 
    if (item) onFollow();
 
504
    Q_UNUSED(item);
 
505
 
 
506
    QList<QAction *> actions = chatNicks->actions();
 
507
    actions.first()->activate(QAction::Trigger);
340
508
}
341
509
 
342
510
void HWChatWidget::chatNickSelected(int index)
386
554
    }
387
555
 
388
556
    items[0]->setData(Qt::UserRole, isReady); // bulb status
389
 
    updateIcon(items[0]);
 
557
    updateNickItem(items[0]);
390
558
 
391
559
    // ensure we're still showing the status bulbs
392
560
    showReady = true;