1 /*
2     Copyright (c) 2020, Lukas Holecek <hluk@email.cz>
3 
4     This file is part of CopyQ.
5 
6     CopyQ is free 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     CopyQ 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 CopyQ.  If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include "clipboardbrowserplaceholder.h"
21 
22 #include "common/common.h"
23 #include "common/log.h"
24 #include "common/timer.h"
25 #include "item/itemstore.h"
26 #include "gui/clipboardbrowser.h"
27 #include "gui/iconfactory.h"
28 #include "gui/icons.h"
29 
30 #include <QPushButton>
31 #include <QVBoxLayout>
32 #include <QWidget>
33 
34 #include <memory>
35 
ClipboardBrowserPlaceholder(const QString & tabName,const ClipboardBrowserSharedPtr & shared,QWidget * parent)36 ClipboardBrowserPlaceholder::ClipboardBrowserPlaceholder(
37         const QString &tabName, const ClipboardBrowserSharedPtr &shared, QWidget *parent)
38     : QWidget(parent)
39     , m_tabName(tabName)
40     , m_sharedData(shared)
41 {
42     auto layout = new QVBoxLayout(this);
43     layout->setContentsMargins(0, 0, 0, 0);
44 
45     initSingleShotTimer( &m_timerExpire, 0, this, &ClipboardBrowserPlaceholder::expire );
46 }
47 
createBrowser()48 ClipboardBrowser *ClipboardBrowserPlaceholder::createBrowser()
49 {
50     if (m_browser)
51         return m_browser;
52 
53     if (m_loadButton)
54         return nullptr;
55 
56     std::unique_ptr<ClipboardBrowser> c( new ClipboardBrowser(m_tabName, m_sharedData, this) );
57     c->setStoreItems(m_storeItems);
58     c->setMaxItemCount(m_maxItemCount);
59 
60     if ( !c->loadItems() ) {
61         createLoadButton();
62         return nullptr;
63     }
64 
65     connect( c.get(), &ClipboardBrowser::itemSelectionChanged,
66              this, &ClipboardBrowserPlaceholder::restartExpiring);
67     connect( c.get(), &ClipboardBrowser::itemsChanged,
68              this, &ClipboardBrowserPlaceholder::restartExpiring);
69 
70     m_browser = c.release();
71     setActiveWidget(m_browser);
72 
73     restartExpiring();
74 
75     emit browserCreated(m_browser);
76     return m_browser;
77 }
78 
setTabName(const QString & tabName)79 bool ClipboardBrowserPlaceholder::setTabName(const QString &tabName)
80 {
81     if ( isEditorOpen() ) {
82         if ( !m_browser->setTabName(tabName) )
83             return false;
84         reloadBrowser();
85     } else {
86         unloadBrowser();
87         if ( !moveItems(m_tabName, tabName) ) {
88             if ( isVisible() )
89                 createBrowser();
90             return false;
91         }
92     }
93 
94     ::removeItems(m_tabName);
95     m_tabName = tabName;
96 
97     if ( isVisible() )
98         createBrowser();
99 
100     return true;
101 }
102 
setMaxItemCount(int count)103 void ClipboardBrowserPlaceholder::setMaxItemCount(int count)
104 {
105     m_maxItemCount = count;
106     if (m_browser)
107         m_browser->setMaxItemCount(count);
108 }
109 
setStoreItems(bool store)110 void ClipboardBrowserPlaceholder::setStoreItems(bool store)
111 {
112     m_storeItems = store;
113     if (m_browser)
114         m_browser->setStoreItems(store);
115 }
116 
removeItems()117 void ClipboardBrowserPlaceholder::removeItems()
118 {
119     unloadBrowser();
120 
121     ::removeItems(m_tabName);
122 }
123 
isDataLoaded() const124 bool ClipboardBrowserPlaceholder::isDataLoaded() const
125 {
126     return m_browser != nullptr;
127 }
128 
createBrowserAgain()129 ClipboardBrowser *ClipboardBrowserPlaceholder::createBrowserAgain()
130 {
131     delete m_loadButton;
132     m_loadButton = nullptr;
133     return createBrowser();
134 }
135 
reloadBrowser()136 void ClipboardBrowserPlaceholder::reloadBrowser()
137 {
138     if ( isEditorOpen() ) {
139         connect( m_browser, &ClipboardBrowser::editingFinished,
140                  this, &ClipboardBrowserPlaceholder::reloadBrowser, Qt::UniqueConnection );
141     } else {
142         unloadBrowser();
143         if ( isVisible() )
144             createBrowser();
145     }
146 }
147 
showEvent(QShowEvent * event)148 void ClipboardBrowserPlaceholder::showEvent(QShowEvent *event)
149 {
150     QWidget::showEvent(event);
151     QTimer::singleShot(0, this, [this](){
152         if ( isVisible() )
153             createBrowser();
154     });
155 }
156 
hideEvent(QHideEvent * event)157 void ClipboardBrowserPlaceholder::hideEvent(QHideEvent *event)
158 {
159     restartExpiring();
160     QWidget::hideEvent(event);
161 }
162 
expire()163 bool ClipboardBrowserPlaceholder::expire()
164 {
165     if (!m_browser)
166         return true;
167 
168     if (canExpire()) {
169         COPYQ_LOG( QString("Tab \"%1\": Expired").arg(m_tabName) );
170         unloadBrowser();
171         return true;
172     }
173 
174     restartExpiring();
175     return false;
176 }
177 
setActiveWidget(QWidget * widget)178 void ClipboardBrowserPlaceholder::setActiveWidget(QWidget *widget)
179 {
180     layout()->addWidget(widget);
181     setFocusProxy(widget);
182     widget->show();
183     if (isVisible())
184         widget->setFocus();
185 }
186 
createLoadButton()187 void ClipboardBrowserPlaceholder::createLoadButton()
188 {
189     if (m_loadButton)
190         return;
191 
192     m_loadButton = new QPushButton(this);
193     m_loadButton->setObjectName("ClipboardBrowserRefreshButton");
194     m_loadButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
195     m_loadButton->setFlat(true);
196 
197     const QIcon icon( getIcon("", IconRedo) );
198     m_loadButton->setIconSize( QSize(64, 64) );
199     m_loadButton->setIcon(icon);
200 
201     connect( m_loadButton, &QAbstractButton::clicked,
202              this, &ClipboardBrowserPlaceholder::createBrowserAgain );
203 
204     setActiveWidget(m_loadButton);
205 }
206 
unloadBrowser()207 void ClipboardBrowserPlaceholder::unloadBrowser()
208 {
209     if (!m_browser)
210         return;
211 
212     COPYQ_LOG( QString("Tab \"%1\": Unloading").arg(m_tabName) );
213 
214     // WORKAROUND: This is needed on macOS, to fix refocusing correct widget later.
215     m_browser->clearFocus();
216 
217     m_browser->hide();
218     m_browser->saveUnsavedItems();
219     m_browser->deleteLater();
220     m_browser = nullptr;
221 
222     emit browserDestroyed();
223 }
224 
canExpire() const225 bool ClipboardBrowserPlaceholder::canExpire() const
226 {
227     return m_browser
228             && m_storeItems
229             && !m_browser->isVisible()
230             && !isEditorOpen();
231 }
232 
restartExpiring()233 void ClipboardBrowserPlaceholder::restartExpiring()
234 {
235     const int expireTimeoutMs = 60000 * m_sharedData->minutesToExpire;
236     if (expireTimeoutMs > 0)
237         m_timerExpire.start(expireTimeoutMs);
238 }
239 
isEditorOpen() const240 bool ClipboardBrowserPlaceholder::isEditorOpen() const
241 {
242     return m_browser && (
243                 m_browser->isInternalEditorOpen()
244                 || m_browser->isExternalEditorOpen() );
245 }
246