-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathtagger.h
412 lines (314 loc) · 11.6 KB
/
tagger.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
/* Copyright © 2014 cat <cat@wolfgirl.org>
* This program is free software. It comes without any warranty, to the extent
* permitted by applicable law. You can redistribute it and/or modify it under
* the terms of the Do What The Fuck You Want To Public License, Version 2, as
* published by Sam Hocevar. See http://www.wtfpl.net/ for more details.
*/
#ifndef WISETAGGER_H
#define WISETAGGER_H
/*! \file tagger.h
* \brief Class \ref Tagger
*/
#include <QVBoxLayout>
#include <QResizeEvent>
#include <QLabel>
#include <QFrame>
#include <QWidget>
#include <QStringList>
#include <QFileSystemWatcher>
#include <QBasicTimer>
#include <memory>
#include <QtMultimediaWidgets/QVideoWidget>
#include <QtMultimedia/QMediaPlayer>
#include <QtMultimedia/QMediaPlaylist>
#include "util/unordered_map_qt.h"
#include "util/tag_fetcher.h"
#include "picture.h"
#include "input.h"
#include "file_queue.h"
/*!
* \brief Main widget of the application.
*
* Contains Picture viewer and TagInput.
*/
class Tagger : public QWidget
{
Q_OBJECT
public:
/// Constructs the Tagger widget.
explicit Tagger(QWidget *_parent = nullptr);
~Tagger() override = default;
/// Open file, session or directory.
bool open(const QString& filename, bool recursive);
/// Open file and enqueue its directory.
bool openFile(const QString& filename, bool recursive);
/// Enqueue directory contents and open first file.
bool openDir(const QString& dirname, bool recursive);
/// Open tagging session from file.
bool openSession(const QString& sfile);
/// Open tagging session from serialized data.
bool openSession(const QByteArray& sdata);
/// Open file with specified index in queue.
bool openFileInQueue(size_t index = 0);
/// Result of rename operation
enum class RenameStatus
{
Cancelled = -1, ///< Rename was cancelled by user.
Failed = 0, ///< Rename failed.
Renamed = 1, ///< Renamed successfully.
NotModified = 2 ///< File name was not modified.
};
Q_ENUM(RenameStatus)
/// Options controlling UI behavior when renaming file.
enum class RenameOption
{
NoOption = 0x0, ///< Default UI behavior.
ForceRename = 0x1, ///< Force rename - do not show rename dialog.
ReopenFile = 0x2 ///< Reopen this file after renaming.
};
/// Bitflags for \ref RenameOption
using RenameOptions = QFlags<RenameOption>;
Q_FLAG(RenameOptions)
/// Options controlling skipping files in queue when selecting next or previous file.
enum class SkipOption
{
DontSkip = 0x0, ///< Default behavior, don't skip any files.
SkipToFixable = 0x1, ///< Skip until fixable file is found.
};
/// Bitflags for \ref SkipOption
using SkipOptions = QFlags<SkipOption>;
Q_FLAG(SkipOptions)
/*!
* \brief Determine whether it is safe to close the application.
* \param status Rename operation status, see Tagger::rename().
*/
static bool canExit(RenameStatus status);
/*!
* \brief Rename current file
* \param options UI behavior modifiers (see \ref RenameOption).
* \retval RenameStatus::Renamed Renamed successfully
* \retval RenameStatus::Cancelled Rename cancelled by user.
* \retval RenameStatus::Failed Failed to rename.
*/
RenameStatus rename(RenameOptions options = RenameOption::NoOption);
/*!
* \brief Ask user to rename current file and opens next.
* \param options UI behavior modifiers (see \ref RenameOption).
* \param skip_option Skip files option (see \ref SkipOption)
*/
void nextFile(RenameOptions options = RenameOption::NoOption,
SkipOptions skip_option = SkipOption::DontSkip);
/*!
* \brief Ask user to rename current file and opens previous.
* \param options UI behavior modifiers (see \ref RenameOption).
* \param skip_option Skip files option (see \ref SkipOption)
*/
void prevFile(RenameOptions options = RenameOption::NoOption,
SkipOptions skip_option = SkipOption::DontSkip);
/*!
* \brief Set tag input text.
*/
void setText(const QString& text);
/// Current tag input text.
QString text() const;
/// Ask user to add tags (maybe replacing current tags?)
void addTags(QString tags, int* tag_count=nullptr);
/// Undo tag input changes and set original tags.
void resetText();
/// Is current file name modified by user.
bool fileModified() const;
/// Is current file renameable.
bool fileRenameable() const;
/// Is the file queue empty.
bool isEmpty() const;
/// Are tag file(s) present.
bool hasTagFile() const;
/// Is current media a video.
bool mediaIsVideo() const;
/// Is current media an animated image, eg. a GIF.
bool mediaIsAnimatedImage() const;
/// Is current media playing.
bool mediaIsPlaying() const;
/// Dimensions of current media.
QSize mediaDimensions() const;
/// Zooming factor of current media.
float mediaZoomFactor() const;
/// Framerate of current media. 0 if not a video.
float mediaFramerate() const;
/// File size of current media.
size_t mediaFileSize() const;
/// Upscale small images to fit the widget size
bool upscalingEnabled() const;
/// Imageboard post URL of current media.
QString postURL() const;
/// Current media path.
QString currentFile() const;
/// Path to directory of current media.
QString currentDir() const;
/// File name of current media.
QString currentFileName() const;
/// Extension of current media.
QString currentFileSuffix() const;
/// File type of current media.
QString currentFileType() const;
/// Last modified date of current media.
QDateTime currentFileLastModified() const;
/// Filter string that filename must match to be selected in queue.
QString queueFilter() const;
/// Returns current edit mode.
EditMode editMode() const noexcept;
/// Returns pointer to current tags completion model.
QAbstractItemModel* completionModel();
/// Reference to FileQueue.
FileQueue& queue();
/// Reference to TagFetcner.
TagFetcher& tag_fetcher();
public slots:
/// Set Tag Input visibility.
void setInputVisible(bool visible);
/// Set Tag input view mode.
void setViewMode(ViewMode view_mode);
/// Ask user to delete currently selected file.
void deleteCurrentFile();
/// Applies tag transformations to current file name.
void fixTags();
/// Fetches tags from associated imageboard.
void fetchTags();
/// Find new set of tag files used for autocomplete.
void reloadTags();
/// Open current tag files set in default editor.
void openTagFilesInEditor();
/// Open dialog to edit temporaty tag files.
void openTempTagFileEditor();
/// Open current tag files set in default file browser.
void openTagFilesInShell();
/// Update configuration from QSettings.
void updateSettings();
/// Pause media playback.
void pauseMedia();
/// Resume media playback.
void playMedia();
/// Set media playback state (playing/paused).
void setMediaPlaying(bool playing);
/// Set media mute state.
void setMediaMuted(bool muted);
/// Set filter string that filename must match to be selected in queue.
void setQueueFilter(QString filter_str);
/// Display status information on the bottom left and right.
void setStatusText(QString left, QString right);
/// Set current edit mode.
void setEditMode(EditMode mode);
/// Upscale small images to fit the widget size
void setUpscalingEnabled(bool enabled);
/// Rotate image 90 degress clockwise (true) or counter-clockwise (false).
void rotateImage(bool clockwise);
signals:
/// Emitted when media file has been successfully opened.
void fileOpened(const QString& file);
/// Emitted when media display size has changed.
void mediaResized();
/// Emitted when session file has been sucessfully opened.
void sessionOpened(const QString& sfile);
/// Emitted when \ref queue() becomes empty.
void cleared();
/*!
* \brief Emitted when file name has been modified by user.
* \param newname New file name.
*/
void tagsEdited(const QString& newname);
/*!
* \brief Emitted when file has been renamed by user.
* \param newname New file name.
*/
void fileRenamed(const QString& newname);
/*!
* \brief Emitted when new tags have been added by user.
* \param newtags List of tags not in tag file that were added for current file.
*/
void newTagsAdded(const QStringList& newtags);
/// Emitted when tag file contents have been changed externally.
void tagFileChanged();
/*!
* \brief Emitted when no tag files were found.
* \param normal_file Normal tag file name.
* \param override_file Override tag file name.
* \param paths List of paths that have been searched for tag files.
*/
void tagFilesNotFound(QString normal_file, QString override_file, QStringList paths);
/*!
* \brief Emitted when conflicting tag files were found.
* \param normal_file Normal tag file name.
* \param override_file Override tag file name.
* \param files List of conflicting tag files.
*/
void tagFilesConflict(QString normal_file, QString override_file, QStringList files);
/*!
* \copydoc TagInput::parseError()
*/
void parseError(QString regex_source, QString error, int column);
/*!
* \brief Emitted on double Escape combination to hide the window to tray.
*/
void hideRequested();
/*!
* \brief Emitted when picture label link is clicked.
*/
void linkActivated(QString link);
protected:
void keyPressEvent(QKeyEvent* e) override;
void timerEvent(QTimerEvent* e) override;
void focusInEvent(QFocusEvent* e) override;
private:
void findTagsFiles(bool force = false);
void reloadTagsContents();
bool loadCurrentFile();
static bool isFileRenameable(const QFileInfo& fi);
bool selectWithFixableTags(int direction);
bool loadFile(size_t index, bool silent = false);
bool loadVideo(const QFileInfo& file);
void hideVideo();
void stopVideo();
void updateNewTagsCounts();
void clear();
void tagsFetched(QString file, QString tags);
void getTagDifference(QStringList old_tags, QStringList new_tags, QString& added, QString& removed, bool show_merge_hint);
QByteArray read_tag_data(const QStringList& tags_files);
RenameStatus updateCaption(RenameOptions options);
QString readCaptionFile(QFileInfo source_file) const;
bool writeCaptionFile(QFileInfo source_file, const QStringList &tags) const;
static constexpr int m_tag_input_layout_margin = 10;
QVBoxLayout m_main_layout;
QVBoxLayout m_tag_input_layout;
QFrame m_separator;
Picture m_picture;
TagInput m_input;
TagFetcher m_fetcher;
QBasicTimer m_hide_request_timer;
FileQueue m_file_queue;
QVideoWidget* m_video = nullptr;
QMediaPlaylist m_playlist;
QMediaPlayer m_player;
QString m_previous_dir;
QStringList m_current_tag_files;
QString m_temp_tags;
QStringList m_original_tags;
QString m_queue_filter_src;
// cache last caption file contents
mutable QString m_last_caption_file_contents;
std::unordered_map<QString, unsigned> m_new_tag_counts;
std::unique_ptr<QFileSystemWatcher> m_fs_watcher;
unsigned m_overall_new_tag_counts = 0u;
int m_nav_direction = 0;
/*!
* \brief Specifies where tags are stored
*/
enum class TagStorage {
Filename, ///< Tags are stored in the filename
CaptionFile ///< Tags are stored in a separate caption file
};
TagStorage m_tag_storage = TagStorage::Filename;
bool m_media_muted = false;
bool m_file_renameable = false;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(Tagger::RenameOptions)
#endif // WISETAGGER_H