fix timestamp in Russian day change message
[quassel.git] / src / qtui / bufferwidget.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2013 by the Quassel Project                        *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) version 3.                                           *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
19  ***************************************************************************/
20
21 #include <QLayout>
22 #include <QKeyEvent>
23 #include <QMenu>
24 #include <QScrollBar>
25
26 #include "action.h"
27 #include "actioncollection.h"
28 #include "bufferwidget.h"
29 #include "chatline.h"
30 #include "chatview.h"
31 #include "chatviewsearchbar.h"
32 #include "chatviewsearchcontroller.h"
33 #include "chatviewsettings.h"
34 #include "client.h"
35 #include "iconloader.h"
36 #include "multilineedit.h"
37 #include "qtui.h"
38 #include "settings.h"
39
40 BufferWidget::BufferWidget(QWidget *parent)
41     : AbstractBufferContainer(parent),
42     _chatViewSearchController(new ChatViewSearchController(this)),
43     _autoMarkerLine(true)
44 {
45     ui.setupUi(this);
46     layout()->setContentsMargins(0, 0, 0, 0);
47     layout()->setSpacing(0);
48     // ui.searchBar->hide();
49
50     _chatViewSearchController->setCaseSensitive(ui.searchBar->caseSensitiveBox()->isChecked());
51     _chatViewSearchController->setSearchSenders(ui.searchBar->searchSendersBox()->isChecked());
52     _chatViewSearchController->setSearchMsgs(ui.searchBar->searchMsgsBox()->isChecked());
53     _chatViewSearchController->setSearchOnlyRegularMsgs(ui.searchBar->searchOnlyRegularMsgsBox()->isChecked());
54
55     connect(ui.searchBar, SIGNAL(searchChanged(const QString &)),
56         _chatViewSearchController, SLOT(setSearchString(const QString &)));
57     connect(ui.searchBar->caseSensitiveBox(), SIGNAL(toggled(bool)),
58         _chatViewSearchController, SLOT(setCaseSensitive(bool)));
59     connect(ui.searchBar->searchSendersBox(), SIGNAL(toggled(bool)),
60         _chatViewSearchController, SLOT(setSearchSenders(bool)));
61     connect(ui.searchBar->searchMsgsBox(), SIGNAL(toggled(bool)),
62         _chatViewSearchController, SLOT(setSearchMsgs(bool)));
63     connect(ui.searchBar->searchOnlyRegularMsgsBox(), SIGNAL(toggled(bool)),
64         _chatViewSearchController, SLOT(setSearchOnlyRegularMsgs(bool)));
65     connect(ui.searchBar->searchUpButton(), SIGNAL(clicked()),
66         _chatViewSearchController, SLOT(highlightPrev()));
67     connect(ui.searchBar->searchDownButton(), SIGNAL(clicked()),
68         _chatViewSearchController, SLOT(highlightNext()));
69
70     connect(ui.searchBar, SIGNAL(hidden()), this, SLOT(setFocus()));
71
72     connect(_chatViewSearchController, SIGNAL(newCurrentHighlight(QGraphicsItem *)),
73         this, SLOT(scrollToHighlight(QGraphicsItem *)));
74
75     ActionCollection *coll = QtUi::actionCollection();
76
77     Action *zoomInChatview = coll->add<Action>("ZoomInChatView", this, SLOT(zoomIn()));
78     zoomInChatview->setText(tr("Zoom In"));
79     zoomInChatview->setIcon(SmallIcon("zoom-in"));
80     zoomInChatview->setShortcut(QKeySequence::ZoomIn);
81
82     Action *zoomOutChatview = coll->add<Action>("ZoomOutChatView", this, SLOT(zoomOut()));
83     zoomOutChatview->setIcon(SmallIcon("zoom-out"));
84     zoomOutChatview->setText(tr("Zoom Out"));
85     zoomOutChatview->setShortcut(QKeySequence::ZoomOut);
86
87     Action *zoomOriginalChatview = coll->add<Action>("ZoomOriginalChatView", this, SLOT(zoomOriginal()));
88     zoomOriginalChatview->setIcon(SmallIcon("zoom-original"));
89     zoomOriginalChatview->setText(tr("Actual Size"));
90     //zoomOriginalChatview->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_0)); // used for RTS switching
91
92     Action *setMarkerLine = coll->add<Action>("SetMarkerLineToBottom", this, SLOT(setMarkerLine()));
93     setMarkerLine->setText(tr("Set Marker Line"));
94     setMarkerLine->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_R));
95
96     Action *jumpToMarkerLine = QtUi::actionCollection("Navigation")->add<Action>("JumpToMarkerLine", this, SLOT(jumpToMarkerLine()));
97     jumpToMarkerLine->setText(tr("Go to Marker Line"));
98     jumpToMarkerLine->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_K));
99
100     ChatViewSettings s;
101     s.initAndNotify("AutoMarkerLine", this, SLOT(setAutoMarkerLine(QVariant)), true);
102 }
103
104
105 BufferWidget::~BufferWidget()
106 {
107     delete _chatViewSearchController;
108     _chatViewSearchController = 0;
109 }
110
111
112 void BufferWidget::setAutoMarkerLine(const QVariant &v)
113 {
114     _autoMarkerLine = v.toBool();
115 }
116
117
118 AbstractChatView *BufferWidget::createChatView(BufferId id)
119 {
120     ChatView *chatView;
121     chatView = new ChatView(id, this);
122     chatView->setBufferContainer(this);
123     _chatViews[id] = chatView;
124     ui.stackedWidget->addWidget(chatView);
125     chatView->setFocusProxy(this);
126     return chatView;
127 }
128
129
130 void BufferWidget::removeChatView(BufferId id)
131 {
132     QWidget *view = _chatViews.value(id, 0);
133     if (!view) return;
134     ui.stackedWidget->removeWidget(view);
135     view->deleteLater();
136     _chatViews.take(id);
137 }
138
139
140 void BufferWidget::showChatView(BufferId id)
141 {
142     if (!id.isValid()) {
143         ui.stackedWidget->setCurrentWidget(ui.page);
144     }
145     else {
146         ChatView *view = qobject_cast<ChatView *>(_chatViews.value(id));
147         Q_ASSERT(view);
148         ui.stackedWidget->setCurrentWidget(view);
149         _chatViewSearchController->setScene(view->scene());
150     }
151 }
152
153
154 void BufferWidget::scrollToHighlight(QGraphicsItem *highlightItem)
155 {
156     ChatView *view = qobject_cast<ChatView *>(ui.stackedWidget->currentWidget());
157     if (view) {
158         view->centerOn(highlightItem);
159     }
160 }
161
162
163 void BufferWidget::zoomIn()
164 {
165     ChatView *view = qobject_cast<ChatView *>(ui.stackedWidget->currentWidget());
166     if (view)
167         view->zoomIn();
168 }
169
170
171 void BufferWidget::zoomOut()
172 {
173     ChatView *view = qobject_cast<ChatView *>(ui.stackedWidget->currentWidget());
174     if (view)
175         view->zoomOut();
176 }
177
178
179 void BufferWidget::zoomOriginal()
180 {
181     ChatView *view = qobject_cast<ChatView *>(ui.stackedWidget->currentWidget());
182     if (view)
183         view->zoomOriginal();
184 }
185
186
187 void BufferWidget::addActionsToMenu(QMenu *menu, const QPointF &pos)
188 {
189     Q_UNUSED(pos);
190     ActionCollection *coll = QtUi::actionCollection();
191     menu->addSeparator();
192     menu->addAction(coll->action("ZoomInChatView"));
193     menu->addAction(coll->action("ZoomOutChatView"));
194     menu->addAction(coll->action("ZoomOriginalChatView"));
195 }
196
197
198 bool BufferWidget::eventFilter(QObject *watched, QEvent *event)
199 {
200     if (event->type() != QEvent::KeyPress)
201         return false;
202
203     QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
204
205     MultiLineEdit *inputLine = qobject_cast<MultiLineEdit *>(watched);
206     if (!inputLine)
207         return false;
208
209     // Intercept copy key presses
210     if (keyEvent == QKeySequence::Copy) {
211         if (inputLine->hasSelectedText())
212             return false;
213         ChatView *view = qobject_cast<ChatView *>(ui.stackedWidget->currentWidget());
214         if (view)
215             view->scene()->selectionToClipboard();
216         return true;
217     }
218
219     // We don't want to steal cursor movement keys if the input line is in multiline mode
220     if (!inputLine->isSingleLine())
221         return false;
222
223     switch (keyEvent->key()) {
224     case Qt::Key_Up:
225     case Qt::Key_Down:
226         if (!(keyEvent->modifiers() & Qt::ShiftModifier))
227             return false;
228     case Qt::Key_PageUp:
229     case Qt::Key_PageDown:
230         // static cast to access public qobject::event
231         return static_cast<QObject *>(ui.stackedWidget->currentWidget())->event(event);
232     default:
233         return false;
234     }
235 }
236
237
238 void BufferWidget::currentChanged(const QModelIndex &current, const QModelIndex &previous)
239 {
240     ChatView *prevView = qobject_cast<ChatView *>(ui.stackedWidget->currentWidget());
241
242     AbstractBufferContainer::currentChanged(current, previous); // switch first to avoid a redraw
243
244     // we need to hide the marker line if it's already/still at the bottom of the view (and not scrolled up)
245     ChatView *curView = qobject_cast<ChatView *>(ui.stackedWidget->currentWidget());
246     if (curView) {
247         BufferId curBufferId = current.data(NetworkModel::BufferIdRole).value<BufferId>();
248         if (curBufferId.isValid()) {
249             MsgId markerMsgId = Client::networkModel()->markerLineMsgId(curBufferId);
250             if (markerMsgId == curView->lastMsgId() && markerMsgId == curView->lastVisibleMsgId())
251                 curView->setMarkerLineVisible(false);
252             else
253                 curView->setMarkerLineVisible(true);
254         }
255     }
256
257     if (prevView && autoMarkerLine())
258         setMarkerLine(prevView, false);
259 }
260
261
262 void BufferWidget::setMarkerLine(ChatView *view, bool allowGoingBack)
263 {
264     if (!view)
265         view = qobject_cast<ChatView *>(ui.stackedWidget->currentWidget());
266     if (!view)
267         return;
268
269     ChatLine *lastLine = view->lastVisibleChatLine();
270     if (lastLine) {
271         QModelIndex idx = lastLine->index();
272         MsgId msgId = idx.data(MessageModel::MsgIdRole).value<MsgId>();
273
274         if (!allowGoingBack) {
275             BufferId bufId = view->scene()->singleBufferId();
276             MsgId oldMsgId = Client::markerLine(bufId);
277             if (oldMsgId.isValid() && msgId <= oldMsgId)
278                 return;
279         }
280         view->setMarkerLine(msgId);
281     }
282 }
283
284
285 void BufferWidget::jumpToMarkerLine(ChatView *view, bool requestBacklog)
286 {
287     if (!view)
288         view = qobject_cast<ChatView *>(ui.stackedWidget->currentWidget());
289     if (!view)
290         return;
291
292     view->jumpToMarkerLine(requestBacklog);
293 }