1 /*
2     Scan Tailor - Interactive post-processing tool for scanned pages.
3     Copyright (C)  Joseph Artsimovich <joseph.artsimovich@gmail.com>
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 3 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, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #ifndef PROJECT_PAGES_H_
20 #define PROJECT_PAGES_H_
21 
22 #include <QMutex>
23 #include <QObject>
24 #include <QString>
25 #include <Qt>
26 #include <cstddef>
27 #include <set>
28 #include <vector>
29 #include "BeforeOrAfter.h"
30 #include "ImageId.h"
31 #include "ImageMetadata.h"
32 #include "NonCopyable.h"
33 #include "PageId.h"
34 #include "PageInfo.h"
35 #include "PageView.h"
36 #include "VirtualFunction.h"
37 #include "ref_countable.h"
38 
39 class ImageFileInfo;
40 class ImageInfo;
41 class OrthogonalRotation;
42 class PageSequence;
43 class RelinkablePath;
44 class AbstractRelinker;
45 class QDomElement;
46 
47 class ProjectPages : public QObject, public ref_countable {
48   Q_OBJECT
49   DECLARE_NON_COPYABLE(ProjectPages)
50 
51  public:
52   enum Pages { ONE_PAGE, TWO_PAGES, AUTO_PAGES };
53 
54   enum LayoutType { ONE_PAGE_LAYOUT, TWO_PAGE_LAYOUT };
55 
56   explicit ProjectPages(Qt::LayoutDirection layout_direction = Qt::LeftToRight);
57 
58   ProjectPages(const std::vector<ImageInfo>& images, Qt::LayoutDirection layout_direction);
59 
60   ProjectPages(const std::vector<ImageFileInfo>& files, Pages pages, Qt::LayoutDirection layout_direction);
61 
62   ~ProjectPages() override;
63 
64   Qt::LayoutDirection layoutDirection() const;
65 
66   PageSequence toPageSequence(PageView view) const;
67 
68   void listRelinkablePaths(const VirtualFunction<void, const RelinkablePath&>& sink) const;
69 
70   /**
71    * \note It's up to the caller to make sure different paths aren't collapsed into one.
72    *       Having the same page more the once in a project is not supported by Scan Tailor.
73    */
74   void performRelinking(const AbstractRelinker& relinker);
75 
76   void setLayoutTypeFor(const ImageId& image_id, LayoutType layout);
77 
78   void setLayoutTypeForAllPages(LayoutType layout);
79 
80   void autoSetLayoutTypeFor(const ImageId& image_id, OrthogonalRotation rotation);
81 
82   void updateImageMetadata(const ImageId& image_id, const ImageMetadata& metadata);
83 
84   static int adviseNumberOfLogicalPages(const ImageMetadata& metadata, OrthogonalRotation rotation);
85 
86   int numImages() const;
87 
88   /**
89    * \brief Insert an image before or after the existing one.
90    *
91    * The caller has to make sure he is not inserting an image that already
92    * exists in this ProjectPages.  Requesting to insert a new image
93    * BEFORE the null one is legal and means inserting it at the end.
94    *
95    * \param new_image The image to insert.
96    * \param before_or_after Whether to insert before or after another image.
97    * \param existing The image we are inserting before or after.
98    * \param view This one only affects what is returned.
99    * \return One or two (or zero, if existing image wasn't found) logical
100    *         page descriptors.  If two are returned, they will be returned
101    *         in the order dependent on the layout direction specified
102    *         at construction time.
103    */
104   std::vector<PageInfo> insertImage(const ImageInfo& new_image,
105                                     BeforeOrAfter before_or_after,
106                                     const ImageId& existing,
107                                     PageView view);
108 
109   void removePages(const std::set<PageId>& pages);
110 
111   /**
112    * \brief Unremoves half-a-page, if the other half is still present.
113    *
114    * \param page_id Left or right sub-page to restore.
115    * \return A PageInfo corresponding to the page restored or
116    *         a null PageInfo if restoring failed.
117    */
118   PageInfo unremovePage(const PageId& page_id);
119 
120   /**
121    * \brief Check if all DPIs are OK, in terms of ImageMetadata::isDpiOK()
122    *
123    * \return true if all DPIs are OK, false if not.
124    */
125   bool validateDpis() const;
126 
127   std::vector<ImageFileInfo> toImageFileInfo() const;
128 
129   void updateMetadataFrom(const std::vector<ImageFileInfo>& files);
130 
131  signals:
132 
133   void modified();
134 
135  private:
136   struct ImageDesc {
137     ImageId id;
138     ImageMetadata metadata;
139     int numLogicalPages;    // 1 or 2
140     bool leftHalfRemoved;   // Both can't be true, and if one is true,
141     bool rightHalfRemoved;  // then numLogicalPages is 1.
142 
143     explicit ImageDesc(const ImageInfo& image_info);
144 
145     ImageDesc(const ImageId& id, const ImageMetadata& metadata, Pages pages);
146 
147     PageId::SubPage logicalPageToSubPage(int logical_page, const PageId::SubPage* sub_pages_in_order) const;
148   };
149 
150   void initSubPagesInOrder(Qt::LayoutDirection layout_direction);
151 
152   void setLayoutTypeForImpl(const ImageId& image_id, LayoutType layout, bool* modified);
153 
154   void setLayoutTypeForAllPagesImpl(LayoutType layout, bool* modified);
155 
156   void autoSetLayoutTypeForImpl(const ImageId& image_id, OrthogonalRotation rotation, bool* modified);
157 
158   void updateImageMetadataImpl(const ImageId& image_id, const ImageMetadata& metadata, bool* modified);
159 
160   std::vector<PageInfo> insertImageImpl(const ImageInfo& new_image,
161                                         BeforeOrAfter before_or_after,
162                                         const ImageId& existing,
163                                         PageView view,
164                                         bool& modified);
165 
166   void removePagesImpl(const std::set<PageId>& pages, bool& modified);
167 
168   PageInfo unremovePageImpl(const PageId& page_id, bool& modified);
169 
170   mutable QMutex m_mutex;
171   std::vector<ImageDesc> m_images;
172   PageId::SubPage m_subPagesInOrder[2];
173 };
174 
175 
176 #endif  // ifndef PROJECT_PAGES_H_
177