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