1 /*
2     Copyright © 2014-2019 by The qTox Project Contributors
3 
4     This file is part of qTox, a Qt-based graphical interface for Tox.
5 
6     qTox is libre software: you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation, either version 3 of the License, or
9     (at your option) any later version.
10 
11     qTox is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with qTox.  If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #ifndef WIDGET_H
21 #define WIDGET_H
22 
23 #include "ui_mainwindow.h"
24 
25 #include <QFileInfo>
26 #include <QMainWindow>
27 #include <QPointer>
28 #include <QSystemTrayIcon>
29 
30 #include "genericchatitemwidget.h"
31 
32 #include "src/audio/iaudiocontrol.h"
33 #include "src/audio/iaudiosink.h"
34 #include "src/core/core.h"
35 #include "src/core/groupid.h"
36 #include "src/core/toxfile.h"
37 #include "src/core/toxid.h"
38 #include "src/core/toxpk.h"
39 #include "src/model/friendmessagedispatcher.h"
40 #include "src/model/groupmessagedispatcher.h"
41 #if DESKTOP_NOTIFICATIONS
42 #include "src/platform/desktop_notifications/desktopnotify.h"
43 #endif
44 
45 #define PIXELS_TO_ACT 7
46 
47 namespace Ui {
48 class MainWindow;
49 }
50 
51 class AddFriendForm;
52 class AlSink;
53 class Camera;
54 class ChatForm;
55 class CircleWidget;
56 class ContentDialog;
57 class ContentLayout;
58 class Core;
59 class FilesForm;
60 class Friend;
61 class FriendChatroom;
62 class FriendListWidget;
63 class FriendWidget;
64 class GenericChatroomWidget;
65 class Group;
66 class GroupChatForm;
67 class GroupChatroom;
68 class GroupInvite;
69 class GroupInviteForm;
70 class GroupWidget;
71 class MaskablePixmapWidget;
72 class ProfileForm;
73 class ProfileInfo;
74 class QActionGroup;
75 class QMenu;
76 class QPushButton;
77 class QSplitter;
78 class QTimer;
79 class SettingsWidget;
80 class SystemTrayIcon;
81 class VideoSurface;
82 class UpdateCheck;
83 class Settings;
84 class IChatLog;
85 class ChatHistory;
86 
87 class Widget final : public QMainWindow
88 {
89     Q_OBJECT
90 
91 private:
92     enum class ActiveToolMenuButton
93     {
94         AddButton,
95         GroupButton,
96         TransferButton,
97         SettingButton,
98         None,
99     };
100 
101     enum class DialogType
102     {
103         AddDialog,
104         TransferDialog,
105         SettingDialog,
106         ProfileDialog,
107         GroupDialog
108     };
109 
110     enum class FilterCriteria
111     {
112         All = 0,
113         Online,
114         Offline,
115         Friends,
116         Groups
117     };
118 
119 public:
120     explicit Widget(IAudioControl& audio, QWidget* parent = nullptr);
121     ~Widget() override;
122     void init();
123     void setCentralWidget(QWidget* widget, const QString& widgetName);
124     QString getUsername();
125     Camera* getCamera();
126     static Widget* getInstance(IAudioControl* audio = nullptr);
127     void showUpdateDownloadProgress();
128     void addFriendDialog(const Friend* frnd, ContentDialog* dialog);
129     void addGroupDialog(Group* group, ContentDialog* dialog);
130     bool newFriendMessageAlert(const ToxPk& friendId, const QString& text, bool sound = true,
131                                bool file = false);
132     bool newGroupMessageAlert(const GroupId& groupId, const ToxPk& authorPk, const QString& message,
133                               bool notify);
134     bool getIsWindowMinimized();
135     void updateIcons();
136 
137     static QString fromDialogType(DialogType type);
138     ContentDialog* createContentDialog() const;
139     ContentLayout* createContentDialog(DialogType type) const;
140 
141     static void confirmExecutableOpen(const QFileInfo& file);
142 
143     void clearAllReceipts();
144 
145     void reloadTheme();
146     static inline QIcon prepareIcon(QString path, int w = 0, int h = 0);
147 
148     bool groupsVisible() const;
149 
150     void resetIcon();
151 
152 public slots:
153     void onShowSettings();
154     void onSeparateWindowClicked(bool separate);
155     void onSeparateWindowChanged(bool separate, bool clicked);
156     void setWindowTitle(const QString& title);
157     void forceShow();
158     void onConnected();
159     void onDisconnected();
160     void onStatusSet(Status::Status status);
161     void onFailedToStartCore();
162     void onBadProxyCore();
163     void onSelfAvatarLoaded(const QPixmap& pic);
164     void setUsername(const QString& username);
165     void setStatusMessage(const QString& statusMessage);
166     void addFriend(uint32_t friendId, const ToxPk& friendPk);
167     void addFriendFailed(const ToxPk& userId, const QString& errorInfo = QString());
168     void onFriendStatusChanged(int friendId, Status::Status status);
169     void onFriendStatusMessageChanged(int friendId, const QString& message);
170     void onFriendDisplayedNameChanged(const QString& displayed);
171     void onFriendUsernameChanged(int friendId, const QString& username);
172     void onFriendAliasChanged(const ToxPk& friendId, const QString& alias);
173     void onFriendMessageReceived(uint32_t friendnumber, const QString& message, bool isAction);
174     void onReceiptReceived(int friendId, ReceiptNum receipt);
175     void onFriendRequestReceived(const ToxPk& friendPk, const QString& message);
176     void onFileReceiveRequested(const ToxFile& file);
177     void onEmptyGroupCreated(uint32_t groupnumber, const GroupId& groupId, const QString& title);
178     void onGroupJoined(int groupNum, const GroupId& groupId);
179     void onGroupInviteReceived(const GroupInvite& inviteInfo);
180     void onGroupInviteAccepted(const GroupInvite& inviteInfo);
181     void onGroupMessageReceived(int groupnumber, int peernumber, const QString& message, bool isAction);
182     void onGroupPeerlistChanged(uint32_t groupnumber);
183     void onGroupPeerNameChanged(uint32_t groupnumber, const ToxPk& peerPk, const QString& newName);
184     void onGroupTitleChanged(uint32_t groupnumber, const QString& author, const QString& title);
185     void titleChangedByUser(const QString& title);
186     void onGroupPeerAudioPlaying(int groupnumber, ToxPk peerPk);
187     void onGroupSendFailed(uint32_t groupnumber);
188     void onFriendTypingChanged(uint32_t friendnumber, bool isTyping);
189     void nextContact();
190     void previousContact();
191     void onFriendDialogShown(const Friend* f);
192     void onGroupDialogShown(Group* g);
193     void toggleFullscreen();
194     void refreshPeerListsLocal(const QString& username);
195     void onUpdateAvailable();
196     void onCoreChanged(Core& core);
197 
198 signals:
199     void friendRequestAccepted(const ToxPk& friendPk);
200     void friendRequested(const ToxId& friendAddress, const QString& message);
201     void statusSet(Status::Status status);
202     void statusSelected(Status::Status status);
203     void usernameChanged(const QString& username);
204     void changeGroupTitle(uint32_t groupnumber, const QString& title);
205     void statusMessageChanged(const QString& statusMessage);
206     void resized();
207     void windowStateChanged(Qt::WindowStates states);
208 
209 private slots:
210     void onAddClicked();
211     void onGroupClicked();
212     void onTransferClicked();
213     void showProfile();
214     void openNewDialog(GenericChatroomWidget* widget);
215     void onChatroomWidgetClicked(GenericChatroomWidget* widget);
216     void onStatusMessageChanged(const QString& newStatusMessage);
217     void removeFriend(const ToxPk& friendId);
218     void copyFriendIdToClipboard(const ToxPk& friendId);
219     void removeGroup(const GroupId& groupId);
220     void setStatusOnline();
221     void setStatusAway();
222     void setStatusBusy();
223     void onIconClick(QSystemTrayIcon::ActivationReason);
224     void onUserAwayCheck();
225     void onEventIconTick();
226     void onTryCreateTrayIcon();
227     void onSetShowSystemTray(bool newValue);
228     void onSplitterMoved(int pos, int index);
229     void friendListContextMenu(const QPoint& pos);
230     void friendRequestsUpdate();
231     void groupInvitesUpdate();
232     void groupInvitesClear();
233     void onDialogShown(GenericChatroomWidget* widget);
234     void outgoingNotification();
235     void onCallEnd();
236     void incomingNotification(uint32_t friendId);
237     void onRejectCall(uint32_t friendId);
238     void onStopNotification();
239     void dispatchFile(ToxFile file);
240     void dispatchFileWithBool(ToxFile file, bool);
241     void dispatchFileSendFailed(uint32_t friendId, const QString& fileName);
242     void connectCircleWidget(CircleWidget& circleWidget);
243     void connectFriendWidget(FriendWidget& friendWidget);
244     void searchCircle(CircleWidget& circleWidget);
245     void updateFriendActivity(const Friend& frnd);
246     void registerContentDialog(ContentDialog& contentDialog) const;
247 
248 private:
249     // QMainWindow overrides
250     bool eventFilter(QObject* obj, QEvent* event) final override;
251     bool event(QEvent* e) final override;
252     void closeEvent(QCloseEvent* event) final override;
253     void changeEvent(QEvent* event) final override;
254     void resizeEvent(QResizeEvent* event) final override;
255     void moveEvent(QMoveEvent* event) final override;
256 
257     bool newMessageAlert(QWidget* currentWindow, bool isActive, bool sound = true, bool notify = true);
258     void setActiveToolMenuButton(ActiveToolMenuButton newActiveButton);
259     void hideMainForms(GenericChatroomWidget* chatroomWidget);
260     Group* createGroup(uint32_t groupnumber, const GroupId& groupId);
261     void removeFriend(Friend* f, bool fake = false);
262     void removeGroup(Group* g, bool fake = false);
263     void saveWindowGeometry();
264     void saveSplitterGeometry();
265     void cycleContacts(bool forward);
266     void searchContacts();
267     void changeDisplayMode();
268     void updateFilterText();
269     FilterCriteria getFilterCriteria() const;
270     static bool filterGroups(FilterCriteria index);
271     static bool filterOnline(FilterCriteria index);
272     static bool filterOffline(FilterCriteria index);
273     void retranslateUi();
274     void focusChatInput();
275     void openDialog(GenericChatroomWidget* widget, bool newWindow);
276     void playNotificationSound(IAudioSink::Sound sound, bool loop = false);
277     void cleanupNotificationSound();
278 
279 private:
280     std::unique_ptr<QSystemTrayIcon> icon;
281     QMenu* trayMenu;
282     QAction* statusOnline;
283     QAction* statusAway;
284     QAction* statusBusy;
285     QAction* actionLogout;
286     QAction* actionQuit;
287     QAction* actionShow;
288 
289     QMenu* filterMenu;
290 
291     QActionGroup* filterGroup;
292     QAction* filterAllAction;
293     QAction* filterOnlineAction;
294     QAction* filterOfflineAction;
295     QAction* filterFriendsAction;
296     QAction* filterGroupsAction;
297 
298     QActionGroup* filterDisplayGroup;
299     QAction* filterDisplayName;
300     QAction* filterDisplayActivity;
301 
302     Ui::MainWindow* ui;
303     QSplitter* centralLayout;
304     QPoint dragPosition;
305     ContentLayout* contentLayout;
306     AddFriendForm* addFriendForm;
307     GroupInviteForm* groupInviteForm;
308 
309     ProfileInfo* profileInfo;
310     ProfileForm* profileForm;
311 
312     QPointer<SettingsWidget> settingsWidget;
313     std::unique_ptr<UpdateCheck> updateCheck; // ownership should be moved outside Widget once non-singleton
314     FilesForm* filesForm;
315     static Widget* instance;
316     GenericChatroomWidget* activeChatroomWidget;
317     FriendListWidget* contactListWidget;
318     MaskablePixmapWidget* profilePicture;
319     bool notify(QObject* receiver, QEvent* event);
320     bool autoAwayActive = false;
321     QTimer* timer;
322     bool eventFlag;
323     bool eventIcon;
324     bool wasMaximized = false;
325     QPushButton* friendRequestsButton;
326     QPushButton* groupInvitesButton;
327     unsigned int unreadGroupInvites;
328     int icon_size;
329 
330     IAudioControl& audio;
331     std::unique_ptr<IAudioSink> audioNotification = nullptr;
332     Settings& settings;
333 
334     QMap<ToxPk, FriendWidget*> friendWidgets;
335     // Shared pointer because qmap copies stuff all over the place
336     QMap<ToxPk, std::shared_ptr<FriendMessageDispatcher>> friendMessageDispatchers;
337     // Stop gap method of linking our friend messages back to a group id.
338     // Eventual goal is to have a notification manager that works on
339     // Messages hooked up to message dispatchers but we aren't there
340     // yet
341     QMap<ToxPk, QMetaObject::Connection> friendAlertConnections;
342     QMap<ToxPk, std::shared_ptr<ChatHistory>> friendChatLogs;
343     QMap<ToxPk, std::shared_ptr<FriendChatroom>> friendChatrooms;
344     QMap<ToxPk, ChatForm*> chatForms;
345 
346     QMap<GroupId, GroupWidget*> groupWidgets;
347     QMap<GroupId, std::shared_ptr<GroupMessageDispatcher>> groupMessageDispatchers;
348 
349     // Stop gap method of linking our group messages back to a group id.
350     // Eventual goal is to have a notification manager that works on
351     // Messages hooked up to message dispatchers but we aren't there
352     // yet
353     QMap<GroupId, QMetaObject::Connection> groupAlertConnections;
354     QMap<GroupId, std::shared_ptr<IChatLog>> groupChatLogs;
355     QMap<GroupId, std::shared_ptr<GroupChatroom>> groupChatrooms;
356     QMap<GroupId, QSharedPointer<GroupChatForm>> groupChatForms;
357     Core* core = nullptr;
358 
359 
360     MessageProcessor::SharedParams sharedMessageProcessorParams;
361 #if DESKTOP_NOTIFICATIONS
362     DesktopNotify notifier;
363 #endif
364 
365 #ifdef Q_OS_MAC
366     QAction* fileMenu;
367     QAction* editMenu;
368     QAction* contactMenu;
369     QMenu* changeStatusMenu;
370     QAction* editProfileAction;
371     QAction* logoutAction;
372     QAction* addContactAction;
373     QAction* nextConversationAction;
374     QAction* previousConversationAction;
375 #endif
376 };
377 
378 bool toxActivateEventHandler(const QByteArray& data);
379 
380 #endif // WIDGET_H
381