1 /*
2 * %kadu copyright begin%
3 * Copyright 2009, 2010, 2011 Piotr Galiszewski (piotr.galiszewski@kadu.im)
4 * Copyright 2012 Wojciech Treter (juzefwt@gmail.com)
5 * Copyright 2010 Tomasz Rostański (rozteck@interia.pl)
6 * Copyright 2011 Piotr Dąbrowski (ultr@ultr.pl)
7 * Copyright 2009 Michał Podsiadlik (michal@kadu.net)
8 * Copyright 2009 Bartłomiej Zimoń (uzi18@o2.pl)
9 * Copyright 2010, 2011, 2012, 2013, 2014 Bartosz Brachaczek (b.brachaczek@gmail.com)
10 * Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015 Rafał Przemysław Malinowski (rafal.przemyslaw.malinowski@gmail.com)
11 * %kadu copyright end%
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License as
15 * published by the Free Software Foundation; either version 2 of
16 * the License, or (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 */
26
27 #include <QtGui/QResizeEvent>
28 #include <QtWidgets/QFileDialog>
29 #include <QtXml/QDomElement>
30
31
32 #include "accounts/account-details.h"
33 #include "buddies/buddy-set.h"
34 #include "chat/chat.h"
35 #include "configuration/configuration-api.h"
36 #include "configuration/configuration.h"
37 #include "configuration/configuration.h"
38 #include "configuration/deprecated-configuration-api.h"
39 #include "contacts/contact-set.h"
40 #include "contacts/contact.h"
41 #include "core/injected-factory.h"
42 #include "gui/actions/action.h"
43 #include "gui/actions/base-action-context.h"
44 #include "gui/configuration/chat-configuration-holder.h"
45 #include "gui/widgets/chat-edit-box-size-manager.h"
46 #include "gui/widgets/chat-widget/chat-widget-actions.h"
47 #include "gui/widgets/chat-widget/chat-widget.h"
48 #include "gui/widgets/color-selector.h"
49 #include "gui/widgets/custom-input.h"
50 #include "gui/widgets/talkable-tree-view.h"
51 #include "gui/widgets/toolbar.h"
52 #include "gui/windows/message-dialog.h"
53 #include "icons/icons-manager.h"
54 #include "identities/identity.h"
55 #include "misc/change-notifier-lock.h"
56 #include "misc/error.h"
57 #include "protocols/protocol.h"
58 #include "protocols/services/chat-image-service.h"
59 #include "status/status-configuration-holder.h"
60 #include "status/status-container-manager.h"
61 #include "debug.h"
62
63 #include "chat-edit-box.h"
64
65 QList<ChatEditBox *> chatEditBoxes;
66
ChatEditBox(const Chat & chat,QWidget * parent)67 ChatEditBox::ChatEditBox(const Chat &chat, QWidget *parent) :
68 MainWindow(new BaseActionContext(this), "chat", parent), CurrentChat(chat)
69 {
70 }
71
~ChatEditBox()72 ChatEditBox::~ChatEditBox()
73 {
74 // disconnect(m_chatWidgetActions->colorSelector(), 0, this, 0);
75 disconnect(InputBox, 0, this, 0);
76
77 chatEditBoxes.removeAll(this);
78 }
79
setChatConfigurationHolder(ChatConfigurationHolder * chatConfigurationHolder)80 void ChatEditBox::setChatConfigurationHolder(ChatConfigurationHolder *chatConfigurationHolder)
81 {
82 m_chatConfigurationHolder = chatConfigurationHolder;
83 }
84
setChatWidgetActions(ChatWidgetActions * chatWidgetActions)85 void ChatEditBox::setChatWidgetActions(ChatWidgetActions *chatWidgetActions)
86 {
87 m_chatWidgetActions = chatWidgetActions;
88 }
89
setIconsManager(IconsManager * iconsManager)90 void ChatEditBox::setIconsManager(IconsManager *iconsManager)
91 {
92 m_iconsManager = iconsManager;
93 }
94
setStatusConfigurationHolder(StatusConfigurationHolder * statusConfigurationHolder)95 void ChatEditBox::setStatusConfigurationHolder(StatusConfigurationHolder *statusConfigurationHolder)
96 {
97 m_statusConfigurationHolder = statusConfigurationHolder;
98 }
99
setStatusContainerManager(StatusContainerManager * statusContainerManager)100 void ChatEditBox::setStatusContainerManager(StatusContainerManager *statusContainerManager)
101 {
102 m_statusContainerManager = statusContainerManager;
103 }
104
init()105 void ChatEditBox::init()
106 {
107 chatEditBoxes.append(this);
108
109 Context = static_cast<BaseActionContext *>(actionContext());
110
111 ChangeNotifierLock lock(Context->changeNotifier());
112
113 RoleSet roles;
114 if (CurrentChat.contacts().size() > 1)
115 roles.insert(ChatRole);
116 else
117 roles.insert(BuddyRole);
118 Context->setRoles(roles);
119
120 Context->setChat(CurrentChat);
121 Context->setContacts(CurrentChat.contacts());
122 Context->setBuddies(CurrentChat.contacts().toBuddySet());
123 updateContext();
124
125 connect(m_statusConfigurationHolder, SIGNAL(setStatusModeChanged()), this, SLOT(updateContext()));
126
127 InputBox = injectedFactory()->makeInjected<CustomInput>(CurrentChat, this);
128 InputBox->setWordWrapMode(QTextOption::WordWrap);
129
130 setCentralWidget(InputBox);
131
132 bool old_top = loadOldToolBarsFromConfig("chatTopDockArea", Qt::TopToolBarArea);
133 bool old_middle = loadOldToolBarsFromConfig("chatMiddleDockArea", Qt::TopToolBarArea);
134 bool old_bottom = loadOldToolBarsFromConfig("chatBottomDockArea", Qt::BottomToolBarArea);
135 bool old_left = loadOldToolBarsFromConfig("chatLeftDockArea", Qt::LeftToolBarArea);
136 bool old_right = loadOldToolBarsFromConfig("chatRightDockArea", Qt::RightToolBarArea);
137
138 if (old_top || old_middle || old_bottom || old_left || old_right)
139 writeToolBarsToConfig(); // port old config
140 else
141 loadToolBarsFromConfig(); // load new config
142
143 // connect(m_chatWidgetActions->colorSelector(), SIGNAL(actionCreated(Action *)),
144 // this, SLOT(colorSelectorActionCreated(Action *)));
145 connect(InputBox, SIGNAL(keyPressed(QKeyEvent *,CustomInput *, bool &)),
146 this, SIGNAL(keyPressed(QKeyEvent *,CustomInput *,bool &)));
147 connect(InputBox, SIGNAL(fontChanged(QFont)), this, SLOT(fontChanged(QFont)));
148 connect(InputBox, SIGNAL(cursorPositionChanged()), this, SLOT(cursorPositionChanged()));
149
150 connect(m_chatConfigurationHolder, SIGNAL(chatConfigurationUpdated()), this, SLOT(configurationUpdated()));
151
152 configurationUpdated();
153 }
154
fontChanged(QFont font)155 void ChatEditBox::fontChanged(QFont font)
156 {
157 if (m_chatWidgetActions->bold()->action(actionContext()))
158 m_chatWidgetActions->bold()->action(actionContext())->setChecked(font.bold());
159 if (m_chatWidgetActions->italic()->action(actionContext()))
160 m_chatWidgetActions->italic()->action(actionContext())->setChecked(font.italic());
161 if (m_chatWidgetActions->underline()->action(actionContext()))
162 m_chatWidgetActions->underline()->action(actionContext())->setChecked(font.underline());
163 }
164
colorSelectorActionCreated(Action * action)165 void ChatEditBox::colorSelectorActionCreated(Action *action)
166 {
167 if (action->parent() == this)
168 setColorFromCurrentText(true);
169 }
170
cursorPositionChanged()171 void ChatEditBox::cursorPositionChanged()
172 {
173 setColorFromCurrentText(false);
174 }
175
configurationUpdated()176 void ChatEditBox::configurationUpdated()
177 {
178 setColorFromCurrentText(true);
179
180 InputBox->setAutoSend(m_chatConfigurationHolder->autoSend());
181 }
182
setAutoSend(bool autoSend)183 void ChatEditBox::setAutoSend(bool autoSend)
184 {
185 InputBox->setAutoSend(autoSend);
186 }
187
inputBox()188 CustomInput * ChatEditBox::inputBox()
189 {
190 return InputBox;
191 }
192
supportsActionType(ActionDescription::ActionType type)193 bool ChatEditBox::supportsActionType(ActionDescription::ActionType type)
194 {
195 return (type == ActionDescription::TypeGlobal || type == ActionDescription::TypeChat || type == ActionDescription::TypeUser);
196 }
197
talkableProxyModel()198 TalkableProxyModel * ChatEditBox::talkableProxyModel()
199 {
200 ChatWidget *cw = chatWidget();
201 if (cw && cw->chat().contacts().count() > 1)
202 return cw->talkableProxyModel();
203
204 return 0;
205 }
206
updateContext()207 void ChatEditBox::updateContext()
208 {
209 if (m_statusConfigurationHolder->isSetStatusPerIdentity())
210 Context->setStatusContainer(CurrentChat.chatAccount().accountIdentity().statusContainer());
211 else if (m_statusConfigurationHolder->isSetStatusPerAccount())
212 Context->setStatusContainer(CurrentChat.chatAccount().statusContainer());
213 else
214 Context->setStatusContainer(m_statusContainerManager);
215 }
216
chatWidget()217 ChatWidget * ChatEditBox::chatWidget()
218 {
219 ChatWidget *result = qobject_cast<ChatWidget *>(parentWidget());
220 if (result)
221 return result;
222
223 result = qobject_cast<ChatWidget *>(parent()->parent());
224 if (result)
225 return result;
226
227 return 0;
228 }
229
createDefaultToolbars(Configuration * configuration,QDomElement toolbarsConfig)230 void ChatEditBox::createDefaultToolbars(Configuration *configuration, QDomElement toolbarsConfig)
231 {
232 QDomElement dockAreaConfig = getDockAreaConfigElement(configuration, toolbarsConfig, "chat_topDockArea");
233 QDomElement toolbarConfig = configuration->api()->createElement(dockAreaConfig, "ToolBar");
234
235 addToolButton(configuration, toolbarConfig, "autoSendAction");
236 addToolButton(configuration, toolbarConfig, "clearChatAction");
237 addToolButton(configuration, toolbarConfig, "insertEmoticonAction", Qt::ToolButtonTextBesideIcon);
238 addToolButton(configuration, toolbarConfig, "insertImageAction");
239 addToolButton(configuration, toolbarConfig, "showHistoryAction");
240 addToolButton(configuration, toolbarConfig, "encryptionAction");
241 addToolButton(configuration, toolbarConfig, "editUserAction");
242 addToolButton(configuration, toolbarConfig, "__spacer1", Qt::ToolButtonTextBesideIcon);
243 addToolButton(configuration, toolbarConfig, "sendAction", Qt::ToolButtonTextBesideIcon);
244 }
245
openColorSelector(const QWidget * activatingWidget)246 void ChatEditBox::openColorSelector(const QWidget *activatingWidget)
247 {
248 //sytuacja podobna jak w przypadku emoticon_selectora
249 ColorSelector *colorSelector = new ColorSelector(InputBox->palette().foreground().color(), activatingWidget, this);
250 connect(colorSelector, SIGNAL(colorSelect(const QColor &)), this, SLOT(changeColor(const QColor &)));
251 colorSelector->show();
252 }
253
openInsertImageDialog()254 void ChatEditBox::openInsertImageDialog()
255 {
256 ChatImageService *chatImageService = CurrentChat.chatAccount().protocolHandler()->chatImageService();
257 if (!chatImageService)
258 return;
259
260 // QTBUG-849
261 QString selectedFile = QFileDialog::getOpenFileName(this, tr("Insert image"), configuration()->deprecatedApi()->readEntry("Chat", "LastImagePath"),
262 tr("Images (*.png *.PNG *.jpg *.JPG *.jpeg *.JPEG *.gif *.GIF *.bmp *.BMP);;All Files (*)"));
263 if (!selectedFile.isEmpty())
264 {
265 QFileInfo f(selectedFile);
266
267 configuration()->deprecatedApi()->writeEntry("Chat", "LastImagePath", f.absolutePath());
268
269 if (!f.isReadable())
270 {
271 MessageDialog::show(m_iconsManager->iconByPath(KaduIcon("dialog-warning")), tr("Kadu"), tr("This file is not readable"), QMessageBox::Ok, this);
272 return;
273 }
274
275 Error imageSizeError = chatImageService->checkImageSize(f.size());
276 if (!imageSizeError.message().isEmpty())
277 {
278 MessageDialog *dialog = MessageDialog::create(m_iconsManager->iconByPath(KaduIcon("dialog-warning")), tr("Kadu"), imageSizeError.message(), this);
279 dialog->addButton(QMessageBox::Yes, tr("Send anyway"));
280 dialog->addButton(QMessageBox::No, tr("Cancel"));
281
282 switch (imageSizeError.severity())
283 {
284 case NoError:
285 break;
286 case ErrorLow:
287 if (dialog->ask())
288 return;
289 break;
290 case ErrorHigh:
291 MessageDialog::show(m_iconsManager->iconByPath(KaduIcon("dialog-error")), tr("Kadu"), imageSizeError.message(), QMessageBox::Ok, this);
292 return;
293 default:
294 break;
295 }
296 }
297
298 int tooBigCounter = 0;
299 int disconnectedCounter = 0;
300
301 foreach (const Contact &contact, CurrentChat.contacts())
302 {
303 if (contact.currentStatus().isDisconnected())
304 disconnectedCounter++;
305 else if (contact.maximumImageSize() == 0 || contact.maximumImageSize() * 1024 < f.size())
306 tooBigCounter++;
307 }
308
309 QString message;
310 if (1 == CurrentChat.contacts().count())
311 {
312 Contact contact = *CurrentChat.contacts().constBegin();
313 if (tooBigCounter > 0)
314 message = tr("This image has %1 KiB and may be too big for %2.")
315 .arg((f.size() + 1023) / 1024).arg(contact.display(true)) + '\n';
316 else if (disconnectedCounter > 0)
317 message = tr("%1 appears to be offline and may not receive images.").arg(contact.display(true)) + '\n';
318 }
319 else
320 {
321 if (tooBigCounter > 0)
322 message = tr("This image has %1 KiB and may be too big for %2 of %3 contacts in this conference.")
323 .arg((f.size() + 1023) / 1024).arg(tooBigCounter).arg(CurrentChat.contacts().count()) + '\n';
324 if (disconnectedCounter > 0)
325 message += tr("%1 of %2 contacts appear to be offline and may not receive images.").arg(disconnectedCounter).arg(CurrentChat.contacts().count()) + '\n';
326 }
327 if (tooBigCounter > 0 || disconnectedCounter > 0)
328 message += tr("Do you really want to send this image?");
329
330 MessageDialog *dialog = MessageDialog::create(m_iconsManager->iconByPath(KaduIcon("dialog-question")), tr("Kadu"), message, this);
331 dialog->addButton(QMessageBox::Yes, tr("Send anyway"));
332 dialog->addButton(QMessageBox::No, tr("Cancel"));
333
334 if (!message.isEmpty() && !dialog->ask())
335 return;
336
337 InputBox->insertHtml(QString("<img src='%1' />").arg(selectedFile));
338 }
339 }
340
changeColor(const QColor & newColor)341 void ChatEditBox::changeColor(const QColor &newColor)
342 {
343 CurrentColor = newColor;
344
345 QPixmap p(12, 12);
346 p.fill(CurrentColor);
347
348 // Action *action = m_chatWidgetActions->colorSelector()->action(this);
349 // if (action)
350 // action->setIcon(p);
351
352 InputBox->setTextColor(CurrentColor);
353 }
354
setColorFromCurrentText(bool force)355 void ChatEditBox::setColorFromCurrentText(bool force)
356 {
357 Q_UNUSED(force);
358
359 /*
360 Action *action = m_chatWidgetActions->colorSelector()->action(this);
361 if (!action || (!force && (InputBox->textColor() == CurrentColor)))
362 return;
363
364 int i;
365 for (i = 0; i < 16; ++i)
366 if (InputBox->textColor() == QColor(colors[i]))
367 break;
368
369 QPixmap p(12, 12);
370 if (i >= 16)
371 CurrentColor = InputBox->palette().foreground().color();
372 else
373 CurrentColor = colors[i];
374
375 p.fill(CurrentColor);
376
377 action->QAction::setIcon(p);
378 */
379 }
380
insertPlainText(const QString & plainText)381 void ChatEditBox::insertPlainText(const QString &plainText)
382 {
383 InputBox->insertPlainText( plainText);
384 }
385
386 #include "moc_chat-edit-box.cpp"
387