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 THUMBNAILSEQUENCE_H_ 20 #define THUMBNAILSEQUENCE_H_ 21 22 #include <QObject> 23 #include <memory> 24 #include <set> 25 #include <vector> 26 #include "BeforeOrAfter.h" 27 #include "FlagOps.h" 28 #include "NonCopyable.h" 29 #include "PageOrderProvider.h" 30 #include "PageRange.h" 31 #include "intrusive_ptr.h" 32 33 class QGraphicsItem; 34 class QGraphicsView; 35 class PageId; 36 class ImageId; 37 class PageInfo; 38 class PageSequence; 39 class ThumbnailFactory; 40 class QSizeF; 41 class QRectF; 42 class QPoint; 43 44 class ThumbnailSequence : public QObject { 45 Q_OBJECT 46 DECLARE_NON_COPYABLE(ThumbnailSequence) 47 48 public: 49 enum SelectionAction { KEEP_SELECTION, RESET_SELECTION }; 50 51 enum SelectionFlags { 52 DEFAULT_SELECTION_FLAGS = 0, 53 54 /** Indicates the item was selected by a user action, rather than programmatically. */ 55 SELECTED_BY_USER = 1 << 0, 56 57 /** 58 * Indicates that the request to make this item a selection leader was redundant, 59 * as it's already a selection leader. 60 */ 61 REDUNDANT_SELECTION = 1 << 1, 62 63 /** 64 * This flag is set when Ctrl-clicking the current selection leader while other 65 * selected items exist. In this case, the leader will become unselected, and 66 * one of the other selected items will be promoted to a selection leader. 67 * In these circumstances, scrolling to make the new selection leader visible 68 * is undesireable. 69 */ 70 AVOID_SCROLLING_TO = 1 << 2 71 }; 72 73 explicit ThumbnailSequence(const QSizeF& max_logical_thumb_size); 74 75 ~ThumbnailSequence() override; 76 77 void setThumbnailFactory(intrusive_ptr<ThumbnailFactory> factory); 78 79 void attachView(QGraphicsView* view); 80 81 /** 82 * \brief Re-populate the list of thumbnails. 83 * 84 * \param pages Pages to put in the sequence. 85 * \param selection_action Whether to keep the selection, provided 86 * selected item(s) are still present in the new list of pages. 87 * \param order_provider The source of ordering information. It will 88 * be preserved until the next reset() call and will be taken 89 * into account by other methods, like invalidateThumbnail() 90 * and insert(). A null order provider indicates to keep the 91 * order of ProjectPages. 92 */ 93 void reset(const PageSequence& pages, 94 SelectionAction selection_action, 95 intrusive_ptr<const PageOrderProvider> order_provider = nullptr); 96 97 /** Returns the current page order provider, which may be null. */ 98 intrusive_ptr<const PageOrderProvider> pageOrderProvider() const; 99 100 PageSequence toPageSequence() const; 101 102 /** 103 * \brief Updates position of all the thumbnails in the view. 104 * 105 * \note This function doesn't update thumbnails appearance. 106 */ 107 void updateSceneItemsPos(); 108 109 /** 110 * \brief Updates appearance and possibly position of a thumbnail. 111 * 112 * If thumbnail's size or position have changed and this thumbnail 113 * is a selection leader, newSelectionLeader() signal will be emitted 114 * with REDUNDANT_SELECTION flag set. 115 * 116 * \note This function assumes the thumbnail specified by page_id 117 * is the only thumbnail at incorrect position. If you do 118 * something that changes the logical position of more than 119 * one thumbnail at once, use invalidateAllThumbnails() 120 * instead of sequentially calling invalidateThumbnail(). 121 */ 122 void invalidateThumbnail(const PageId& page_id); 123 124 /** 125 * This signature differs from invalidateThumbnail(PageId) in that 126 * it will cause PageInfo stored by ThumbnailSequence to be updated. 127 */ 128 void invalidateThumbnail(const PageInfo& page_info); 129 130 /** 131 * \brief Updates appearance of all thumbnails and possibly their order. 132 * 133 * Whether or not order will be updated depends on whether an order provider 134 * was specified by the most recent reset() call. 135 */ 136 void invalidateAllThumbnails(); 137 138 /** 139 * \brief Makes the item a selection leader, and unselects other items. 140 * 141 * \param page_id The page to select. 142 * \param selection_action Whether to keep the selection, provided 143 * selected item(s) are still present in the new list of pages. 144 * \return true on success, false if the requested page wasn't found. 145 * 146 * On success, the newSelectionLeader() signal is emitted, possibly 147 * with REDUNDANT_SELECTION flag set, in case our page was already the 148 * selection leader. 149 */ 150 bool setSelection(const PageId& page_id, SelectionAction selection_action = RESET_SELECTION); 151 152 /** 153 * \brief Returns the current selection leader. 154 * 155 * A null PageInfo is returned if no items are currently selected. 156 */ 157 PageInfo selectionLeader() const; 158 159 /** 160 * \brief Returns the page immediately preceding the given one. 161 * 162 * A null PageInfo is returned if the given page wasn't found or 163 * there are no pages preceding it. 164 */ 165 PageInfo prevPage(const PageId& reference_page) const; 166 167 /** 168 * \brief Returns the page immediately following the given one. 169 * 170 * A null PageInfo is returned if the given page wasn't found or 171 * there are no pages following it. 172 */ 173 PageInfo nextPage(const PageId& reference_page) const; 174 175 /** 176 * \brief Returns the selected page preceding the given one. 177 * 178 * A null PageInfo is returned if the given page wasn't found or 179 * there are no pages preceding it. 180 */ 181 PageInfo prevSelectedPage(const PageId& reference_page) const; 182 183 /** 184 * \brief Returns the selected page following the given one. 185 * 186 * A null PageInfo is returned if the given page wasn't found or 187 * there are no pages following it. 188 */ 189 PageInfo nextSelectedPage(const PageId& reference_page) const; 190 191 /** 192 * \brief Returns the first page in the sequence. 193 * 194 * A null PageInfo is returned if the sequence is empty. 195 */ 196 PageInfo firstPage() const; 197 198 /** 199 * \brief Returns the last page in the sequence. 200 * 201 * A null PageInfo is returned if the sequence is empty. 202 */ 203 PageInfo lastPage() const; 204 205 /** 206 * \brief Inserts a page before the first page with matching ImageId. 207 * 208 * If no order provider was specified by the previous reset() call, 209 * we won't allow inserting a page between two halves of another page, 210 * to be compatible with what reset() does. Otherwise, the new 211 * page will be inserted at a correct position according to the current 212 * order provider. In this case \p before_or_after doesn't really matter. 213 * 214 * If there are no pages with matching ImageId, the new page won't 215 * be inserted, unless the request is to insert BEFORE a null ImageId(), 216 * which would cause insertion at the end. 217 */ 218 void insert(const PageInfo& new_page, BeforeOrAfter before_or_after, const ImageId& image); 219 220 void removePages(const std::set<PageId>& pages); 221 222 /** 223 * \brief The bounding rectangle in scene coordinates of the selection leader. 224 * 225 * Returns a null rectangle if no item is currently selected. 226 */ 227 QRectF selectionLeaderSceneRect() const; 228 229 std::set<PageId> selectedItems() const; 230 231 std::vector<PageRange> selectedRanges() const; 232 233 const QSizeF& getMaxLogicalThumbSize() const; 234 235 void setMaxLogicalThumbSize(const QSizeF& size); 236 237 signals: 238 239 void newSelectionLeader(const PageInfo& page_info, const QRectF& thumb_rect, ThumbnailSequence::SelectionFlags flags); 240 241 /** 242 * Emitted when a user right-clicks on a page thumbnail. 243 */ 244 void pageContextMenuRequested(const PageInfo& page_info, const QPoint& screen_pos, bool selected); 245 246 /** 247 * Emitted when a user right clicks on area below the last page. 248 * In the absence of any pages, all the area is considered to be 249 * below the last page. 250 */ 251 void pastLastPageContextMenuRequested(const QPoint& screen_pos); 252 253 private: 254 class Item; 255 class Impl; 256 class GraphicsScene; 257 class PlaceholderThumb; 258 class LabelGroup; 259 class CompositeItem; 260 261 void emitNewSelectionLeader(const PageInfo& page_info, const CompositeItem* composite, SelectionFlags flags); 262 263 std::unique_ptr<Impl> m_impl; 264 }; 265 266 267 DEFINE_FLAG_OPS(ThumbnailSequence::SelectionFlags) 268 269 #endif // ifndef THUMBNAILSEQUENCE_H_ 270