1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include "MasterPageContainerQueue.hxx"
21 #include "MasterPageContainerProviders.hxx"
22 
23 #include <tools/IdleDetection.hxx>
24 
25 #include <set>
26 
27 namespace sd { namespace sidebar {
28 
29 const sal_Int32 MasterPageContainerQueue::snDelayedCreationTimeout (15);
30 const sal_Int32 MasterPageContainerQueue::snDelayedCreationTimeoutWhenNotIdle (100);
31 const sal_Int32 MasterPageContainerQueue::snMasterPagePriorityBoost (5);
32 const sal_Int32 MasterPageContainerQueue::snWaitForMoreRequestsPriorityThreshold (-10);
33 sal_uInt32 MasterPageContainerQueue::snWaitForMoreRequestsCount(15);
34 
35 //===== MasterPageContainerQueue::PreviewCreationRequest ======================
36 
37 class MasterPageContainerQueue::PreviewCreationRequest
38 {
39 public:
PreviewCreationRequest(const SharedMasterPageDescriptor & rpDescriptor,int nPriority)40     PreviewCreationRequest (const SharedMasterPageDescriptor& rpDescriptor, int nPriority)
41         : mpDescriptor(rpDescriptor),
42           mnPriority(nPriority)
43     {}
44     SharedMasterPageDescriptor const mpDescriptor;
45     int const mnPriority;
46     class Compare
47     {
48     public:
operator ()(const PreviewCreationRequest & r1,const PreviewCreationRequest & r2) const49         bool operator() (const PreviewCreationRequest& r1,const PreviewCreationRequest& r2) const
50         {
51             if (r1.mnPriority != r2.mnPriority)
52             {
53                 // Prefer requests with higher priority.
54                 return r1.mnPriority > r2.mnPriority;
55             }
56             else
57             {
58                 // Prefer tokens that have been earlier created (those with lower
59                 // value).
60                 return r1.mpDescriptor->maToken < r2.mpDescriptor->maToken;
61             }
62         }
63     };
64     class CompareToken
65     {
66     public:
67         MasterPageContainer::Token const maToken;
CompareToken(MasterPageContainer::Token aToken)68         explicit CompareToken(MasterPageContainer::Token aToken) : maToken(aToken) {}
operator ()(const PreviewCreationRequest & rRequest) const69         bool operator() (const PreviewCreationRequest& rRequest) const
70             { return maToken==rRequest.mpDescriptor->maToken; }
71     };
72 };
73 
74 //===== MasterPageContainerQueue::RequestQueue ================================
75 
76 class MasterPageContainerQueue::RequestQueue
77     : public ::std::set<PreviewCreationRequest,PreviewCreationRequest::Compare>
78 {
79 public:
RequestQueue()80     RequestQueue() {}
81 };
82 
83 //===== MasterPageContainerQueue ==============================================
84 
Create(const std::weak_ptr<ContainerAdapter> & rpContainer)85 MasterPageContainerQueue* MasterPageContainerQueue::Create (
86     const std::weak_ptr<ContainerAdapter>& rpContainer)
87 {
88     MasterPageContainerQueue* pQueue = new MasterPageContainerQueue(rpContainer);
89     pQueue->LateInit();
90     return pQueue;
91 }
92 
MasterPageContainerQueue(const std::weak_ptr<ContainerAdapter> & rpContainer)93 MasterPageContainerQueue::MasterPageContainerQueue (
94     const std::weak_ptr<ContainerAdapter>& rpContainer)
95     : mpWeakContainer(rpContainer),
96       mpRequestQueue(new RequestQueue()),
97       maDelayedPreviewCreationTimer(),
98       mnRequestsServedCount(0)
99 {
100 }
101 
~MasterPageContainerQueue()102 MasterPageContainerQueue::~MasterPageContainerQueue()
103 {
104     maDelayedPreviewCreationTimer.Stop();
105     while ( ! mpRequestQueue->empty())
106         mpRequestQueue->erase(mpRequestQueue->begin());
107 }
108 
LateInit()109 void MasterPageContainerQueue::LateInit()
110 {
111     // Set up the timer for the delayed creation of preview bitmaps.
112     maDelayedPreviewCreationTimer.SetTimeout (snDelayedCreationTimeout);
113     maDelayedPreviewCreationTimer.SetInvokeHandler(
114         LINK(this,MasterPageContainerQueue,DelayedPreviewCreation) );
115 }
116 
RequestPreview(const SharedMasterPageDescriptor & rpDescriptor)117 bool MasterPageContainerQueue::RequestPreview (const SharedMasterPageDescriptor& rpDescriptor)
118 {
119     bool bSuccess (false);
120     if (rpDescriptor.get() != nullptr
121         && rpDescriptor->maLargePreview.GetSizePixel().Width() == 0)
122     {
123         sal_Int32 nPriority (CalculatePriority(rpDescriptor));
124 
125         // Add a new or replace an existing request.
126         RequestQueue::iterator iRequest (::std::find_if(
127             mpRequestQueue->begin(),
128             mpRequestQueue->end(),
129             PreviewCreationRequest::CompareToken(rpDescriptor->maToken)));
130         // When a request for the same token exists then the lowest of the
131         // two priorities is used.
132         if (iRequest != mpRequestQueue->end())
133             if (iRequest->mnPriority < nPriority)
134             {
135                 mpRequestQueue->erase(iRequest);
136                 iRequest = mpRequestQueue->end();
137             }
138 
139         // Add a new request when none exists (or has just been erased).
140         if (iRequest == mpRequestQueue->end())
141         {
142             mpRequestQueue->insert(PreviewCreationRequest(rpDescriptor,nPriority));
143             maDelayedPreviewCreationTimer.Start();
144             bSuccess = true;
145         }
146     }
147     return bSuccess;
148 }
149 
CalculatePriority(const SharedMasterPageDescriptor & rpDescriptor)150 sal_Int32 MasterPageContainerQueue::CalculatePriority (
151     const SharedMasterPageDescriptor& rpDescriptor)
152 {
153     sal_Int32 nPriority;
154 
155     // The cost is used as a starting value.
156     int nCost (0);
157     if (rpDescriptor->mpPreviewProvider != nullptr)
158     {
159         nCost = rpDescriptor->mpPreviewProvider->GetCostIndex();
160         if (rpDescriptor->mpPreviewProvider->NeedsPageObject())
161             if (rpDescriptor->mpPageObjectProvider != nullptr)
162                 nCost += rpDescriptor->mpPageObjectProvider->GetCostIndex();
163     }
164 
165     // Its negative value is used so that requests with a low cost are
166     // preferred over those with high costs.
167     nPriority = -nCost;
168 
169     // Add a term that introduces an order based on the appearance in the
170     // AllMasterPagesSelector.
171     nPriority -= rpDescriptor->maToken / 3;
172 
173     // Process requests for the CurrentMasterPagesSelector first.
174     if (rpDescriptor->meOrigin == MasterPageContainer::MASTERPAGE)
175         nPriority += snMasterPagePriorityBoost;
176 
177     return nPriority;
178 }
179 
IMPL_LINK(MasterPageContainerQueue,DelayedPreviewCreation,Timer *,pTimer,void)180 IMPL_LINK(MasterPageContainerQueue, DelayedPreviewCreation, Timer*, pTimer, void)
181 {
182     bool bIsShowingFullScreenShow (false);
183     bool bWaitForMoreRequests (false);
184 
185     do
186     {
187         if (mpRequestQueue->empty())
188             break;
189 
190         // First check whether the system is idle.
191         tools::IdleState nIdleState (tools::IdleDetection::GetIdleState(nullptr));
192         if (nIdleState != tools::IdleState::Idle)
193         {
194             if (nIdleState & tools::IdleState::FullScreenShowActive)
195                 bIsShowingFullScreenShow = true;
196             break;
197         }
198 
199         PreviewCreationRequest aRequest (*mpRequestQueue->begin());
200 
201         // Check if the request should really be processed right now.
202         // Reasons to not do it are when its cost is high and not many other
203         // requests have been inserted into the queue that would otherwise
204         // be processed first.
205         if (aRequest.mnPriority < snWaitForMoreRequestsPriorityThreshold
206             && (mnRequestsServedCount+mpRequestQueue->size() < snWaitForMoreRequestsCount))
207         {
208             // Wait for more requests before this one is processed.  Note
209             // that the queue processing is not started anew when this
210             // method is left.  That is done when the next request is
211             // inserted.
212             bWaitForMoreRequests = true;
213             break;
214         }
215 
216         mpRequestQueue->erase(mpRequestQueue->begin());
217 
218         if (aRequest.mpDescriptor.get() != nullptr)
219         {
220             mnRequestsServedCount += 1;
221             if ( ! mpWeakContainer.expired())
222             {
223                 std::shared_ptr<ContainerAdapter> pContainer (mpWeakContainer);
224                 if (pContainer != nullptr)
225                     pContainer->UpdateDescriptor(aRequest.mpDescriptor,false,true,true);
226             }
227         }
228     }
229     while (false);
230 
231     if (!mpRequestQueue->empty() && ! bWaitForMoreRequests)
232     {
233         int nTimeout (snDelayedCreationTimeout);
234         if (bIsShowingFullScreenShow)
235             nTimeout = snDelayedCreationTimeoutWhenNotIdle;
236         maDelayedPreviewCreationTimer.SetTimeout(nTimeout);
237         pTimer->Start();
238     }
239 }
240 
HasRequest(MasterPageContainer::Token aToken) const241 bool MasterPageContainerQueue::HasRequest (MasterPageContainer::Token aToken) const
242 {
243     return std::any_of(
244         mpRequestQueue->begin(),
245         mpRequestQueue->end(),
246         PreviewCreationRequest::CompareToken(aToken));
247 }
248 
IsEmpty() const249 bool MasterPageContainerQueue::IsEmpty() const
250 {
251     return mpRequestQueue->empty();
252 }
253 
ProcessAllRequests()254 void MasterPageContainerQueue::ProcessAllRequests()
255 {
256     snWaitForMoreRequestsCount = 0;
257     if (!mpRequestQueue->empty())
258         maDelayedPreviewCreationTimer.Start();
259 }
260 
261 } } // end of namespace sd::sidebar
262 
263 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
264