1 /***************************************************************************
2  *   Copyright (C) 2006-2021 by Ilya Kotov                                 *
3  *   forkotov02@ya.ru                                                      *
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) any later version.                                   *
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 <QAction>
22 #include <QScreen>
23 #include <QWindow>
24 #include <QtDebug>
25 #include <QApplication>
26 #if (QT_VERSION < QT_VERSION_CHECK(5, 7, 0)) //qAsConst template
27 #include <qmmp/qmmp.h>
28 #endif
29 #include "dock.h"
30 
31 Dock *Dock::m_instance = nullptr;
32 
instance()33 Dock *Dock::instance()
34 {
35     if (!m_instance)
36         m_instance = new Dock();
37     return m_instance;
38 }
39 
Dock(QObject * parent)40 Dock::Dock (QObject *parent)
41         : QObject (parent)
42 {
43     m_instance = this;
44     m_mainWidget = nullptr;
45 }
46 
~Dock()47 Dock::~Dock()
48 {
49     m_instance = nullptr;
50 }
51 
setMainWidget(QWidget * widget)52 void Dock::setMainWidget (QWidget *widget)
53 {
54     m_mainWidget = widget;
55     m_widgetList.prepend (widget);
56     m_dockedList.prepend (false);
57 }
58 
snapDesktop(QPoint npos,QWidget * mv)59 QPoint Dock::snapDesktop(QPoint npos, QWidget* mv)
60 {
61     if(!mv->isVisible())
62         return npos;
63 
64     QRect desktopRect = mv->window()->windowHandle()->screen()->availableGeometry();
65     int nx = abs (npos.x() - desktopRect.x()); //left-top
66     int ny = abs (npos.y() - desktopRect.y());
67 
68     if(nx < 13)
69         npos.rx() = desktopRect.x();
70     if(ny < 13)
71         npos.ry() = desktopRect.y();
72 
73     nx = abs (npos.x() + mv->width() - desktopRect.width() - desktopRect.x()); //right-bottom
74     ny = abs (npos.y() + mv->height() - desktopRect.height() - desktopRect.y());
75 
76     if(nx < 13)
77         npos.rx() = desktopRect.width() - mv->width() + desktopRect.x();
78     if(ny < 13)
79         npos.ry() = desktopRect.height() - mv->height() + desktopRect.y();
80 
81     return npos;
82 }
83 
snap(QPoint npos,QWidget * mv,QWidget * st)84 QPoint Dock::snap (QPoint npos, QWidget* mv, QWidget* st)
85 {
86     int nx = npos.x() - st->x();
87     int ny = abs (npos.y() - st->y() + mv->height());
88 
89     if (abs (nx) < 13 && ny < 13) //above
90         npos.rx() = st->x();
91     if (ny < 13 && nx > -mv->width() && nx < st->width())
92         npos.ry() = st->y() - mv->height();
93     nx = abs (npos.x() + mv->width() - st->x() - st->width());
94     if (nx < 13 && ny < 13)
95         npos.rx() = st->x() + st->width() - mv->width();
96 
97     /***********/
98     nx = npos.x() - st->x();
99     ny = abs (npos.y() - st->y() - st->height());
100 
101     if (abs (nx) < 13 && ny < 13) //near
102         npos.rx() = st->x();
103     if (ny < 13 && nx > -mv->width() && nx < st->width())
104         npos.ry() = st->y() + st->height();
105     nx = abs (npos.x() + mv->width() - st->x() - st->width());
106     if (nx < 13 && ny < 13)
107         npos.rx() = st->x() + st->width() - mv->width();
108     /**************/
109     nx = abs (npos.x() - st->x() + mv->width());
110     ny = npos.y() - st->y();
111 
112     if (nx < 13 && abs (ny) < 13) //left
113         npos.ry() = st->y();
114     if (nx < 13 && ny > -mv->height() && ny < st->height())
115         npos.rx() = st->x() - mv->width();
116 
117     ny = abs (npos.y() + mv->height() - st->y() - st->height());
118     if (nx < 13 && ny < 13)
119         npos.ry() = st->y() + st->height() - mv->height();
120     /*****************/
121     nx = abs (npos.x() - st->x() - st->width());
122     ny = npos.y() - st->y();
123 
124     if (nx < 13 && abs (ny) < 13) //right
125         npos.ry() = st->y();
126     if (nx < 13 && ny > -mv->height() && ny < st->height())
127         npos.rx() = st->x() + st->width();
128 
129     ny = abs (npos.y() + mv->height() - st->y() - st->height());
130     if (nx < 13 && ny < 13)
131         npos.ry() = st->y() + st->height() - mv->height();
132 
133     return (npos);
134 }
135 
addWidget(QWidget * widget)136 void Dock::addWidget (QWidget *widget)
137 {
138     m_widgetList.append (widget);
139     m_dockedList.append (false);
140     if(m_mainWidget)
141         widget->addActions(m_mainWidget->actions());
142 }
143 
move(QWidget * mv,QPoint npos)144 void Dock::move (QWidget* mv, QPoint npos)
145 {
146     //QRect desktopRect = QApplication::desktop()->availableGeometry(m_mainWidget);
147 
148     /*if(npos.y() < desktopRect.y())
149         npos.setY(desktopRect.y());*/
150 
151     if (mv == m_mainWidget)
152     {
153 
154         for (int i = 1; i<m_widgetList.size(); ++i)
155         {
156             if (!m_dockedList.at (i))
157             {
158                 if (m_widgetList.at (i)->isVisible())
159                     npos = snap (npos, mv, m_widgetList.at (i));
160             }
161             else
162             {
163                 QPoint pos = npos + m_delta_list.at(i);
164                 for (int j = 1; j<m_widgetList.size(); ++j)
165                 {
166                     if (!m_dockedList.at (j) && m_widgetList.at (j)->isVisible())
167                     {
168                         pos = snap (pos, m_widgetList.at (i), m_widgetList.at (j));
169                         npos = pos - m_delta_list.at(i);
170                     }
171                 }
172             }
173         }
174         npos = snapDesktop(npos, mv);
175         for (int i = 1; i<m_widgetList.size(); ++i)
176         {
177             if (m_dockedList.at (i))
178             {
179                 QPoint pos = npos + m_delta_list.at(i);
180                 pos = snapDesktop(pos, m_widgetList.at(i));
181                 m_widgetList.at (i)->move(pos);
182                 npos = pos - m_delta_list.at(i);
183             }
184         }
185         mv->move (npos);
186     }
187     else
188     {
189         for (int i = 0; i<m_widgetList.size(); ++i)
190         {
191             m_dockedList[i] = false;
192             if (mv != m_widgetList.at (i) && !m_dockedList.at (i) && m_widgetList.at (i)->isVisible())
193             {
194                 npos = snap (npos, mv, m_widgetList.at (i));
195                 npos = snapDesktop(npos, mv);
196             }
197         }
198         mv->move (npos);
199     }
200 }
201 
calculateDistances()202 void Dock::calculateDistances()
203 {
204     m_delta_list.clear();
205     for(const QWidget *w : qAsConst(m_widgetList))
206     {
207         if (w == m_mainWidget)
208             m_delta_list.append(QPoint(0,0));
209         else
210             m_delta_list.append(w->pos() - m_mainWidget->pos());
211     }
212 }
213 
updateDock()214 void Dock::updateDock()
215 {
216     QWidget *mv = m_widgetList.at (0);
217     for (int j = 1; j<m_widgetList.size(); ++j)
218     {
219         QWidget *st = m_widgetList.at (j);
220         m_dockedList[j] = isDocked (mv, st);
221     }
222     for (int j = 1; j<m_widgetList.size(); ++j)
223     {
224         if (m_dockedList[j])
225             for (int i = 1; i<m_widgetList.size(); ++i)
226             {
227                 if (!m_dockedList[i])
228                 {
229                     mv = m_widgetList.at (j);
230                     QWidget *st = m_widgetList.at (i);
231                     m_dockedList[i] = isDocked (mv, st);
232                 }
233             }
234     }
235 }
236 
isDocked(QWidget * mv,QWidget * st)237 bool Dock::isDocked (QWidget* mv, QWidget* st)
238 {
239     int nx = mv->x() - st->x();
240     int ny = abs (mv->y() - st->y() + mv->height());
241     if (ny < 2 && nx > -mv->width() && nx < st->width()) //above
242         return true;
243 
244     /***********/
245     nx = mv->x() - st->x();
246     ny = abs (mv->y() - st->y() - st->height());
247     if (ny < 2 && nx > -mv->width() && nx < st->width()) //near
248         return true;
249 
250     /**************/
251     nx = abs (mv->x() - st->x() + mv->width());
252     ny = mv->y() - st->y();
253     if (nx < 2 && ny > -mv->height() && ny < st->height())   //left
254         return true;
255 
256     /*****************/
257     nx = abs (mv->x() - st->x() - st->width());
258     ny = mv->y() - st->y();
259     if (nx < 2 && ny > -mv->height() && ny < st->height())   //right
260         return true;
261     return false;
262 }
263 
addActions(QList<QAction * > actions)264 void Dock::addActions (QList<QAction *> actions)
265 {
266     if(!m_mainWidget)
267     {
268         qFatal("Dock: main widget is null");
269     }
270     for (int i = 0; i<m_widgetList.size(); ++i)
271         m_widgetList.at (i)->addActions (actions);
272 }
273 
isUnder(QWidget * upper,QWidget * nether,int dy)274 bool Dock::isUnder(QWidget* upper, QWidget* nether, int dy)
275 {
276     int nx = upper->x() - nether->x();
277     return abs (upper->y() + upper->height() -dy - nether->y()) < 2 &&
278            nx > -upper->width() && nx < nether->width();
279 }
280 
align(QWidget * w,int dy)281 void Dock::align(QWidget* w, int dy)
282 {
283     for (int i = 0; i<m_dockedList.size(); ++i)
284     {
285         if (m_widgetList.at(i) != w && isUnder(w, m_widgetList.at(i), dy))
286         {
287             m_widgetList.at(i)->move(m_widgetList.at(i)->x(), m_widgetList.at(i)->y()+dy);
288             align(m_widgetList.at(i), dy);
289         }
290     }
291 }
292