forked from sumatrapdfreader/sumatrapdf
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathFileHistory.cpp
More file actions
182 lines (166 loc) · 6.19 KB
/
Copy pathFileHistory.cpp
File metadata and controls
182 lines (166 loc) · 6.19 KB
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
/* Copyright 2015 the SumatraPDF project authors (see AUTHORS file).
License: GPLv3 */
// utils
#include "BaseUtil.h"
#include "FileUtil.h"
// layout controllers
#include "BaseEngine.h"
#include "SettingsStructs.h"
#include "FileHistory.h"
#include "GlobalPrefs.h"
/* Handling of file history list.
We keep a mostly infinite list of all (still existing in the file system)
files that a user has ever opened. For each file we also keep a bunch of
attributes describing the display state at the time the file was closed.
We persist this list inside preferences file to something looking like this:
FileStates [
FilePath = C:\path\to\file.pdf
DisplayMode = single page
PageNo = 1
ZoomVirtual = 123.4567
Window State = 2
...
]
etc...
We deserialize this info at startup and serialize when the application
quits.
*/
// maximum number of files to remember in total
// (to keep the settings file within reasonable bounds)
#define FILE_HISTORY_MAX_FILES 1000
// sorts the most often used files first
static int cmpOpenCount(const void *a, const void *b) {
DisplayState *dsA = *(DisplayState **) a;
DisplayState *dsB = *(DisplayState **) b;
// sort pinned documents before unpinned ones
if (dsA->isPinned != dsB->isPinned)
return dsA->isPinned ? -1 : 1;
// sort pinned documents alphabetically
if (dsA->isPinned)
return str::CmpNatural(path::GetBaseName(dsA->filePath), path::GetBaseName(dsB->filePath));
// sort often opened documents first
if (dsA->openCount != dsB->openCount)
return dsB->openCount - dsA->openCount;
// use recency as the criterion in case of equal open counts
return dsA->index < dsB->index ? -1 : 1;
}
void FileHistory::Clear(bool keepFavorites) {
if (!states)
return;
Vec<DisplayState *> keep;
for (size_t i = 0; i < states->Count(); i++) {
if (keepFavorites && states->At(i)->favorites->Count() > 0) {
states->At(i)->openCount = 0;
keep.Append(states->At(i));
} else {
DeleteDisplayState(states->At(i));
}
}
*states = keep;
}
DisplayState *FileHistory::Get(size_t index) const {
if (index < states->Count())
return states->At(index);
return nullptr;
}
DisplayState *FileHistory::Find(const WCHAR *filePath, size_t *idxOut) const {
for (size_t i = 0; i < states->Count(); i++) {
if (str::EqI(states->At(i)->filePath, filePath)) {
if (idxOut)
*idxOut = i;
return states->At(i);
}
}
return nullptr;
}
DisplayState *FileHistory::MarkFileLoaded(const WCHAR *filePath) {
CrashIf(!filePath);
// if a history entry with the same name already exists,
// then reuse it. That way we don't have duplicates and
// the file moves to the front of the list
DisplayState *state = Find(filePath);
if (!state) {
state = NewDisplayState(filePath);
state->useDefaultState = true;
} else {
states->Remove(state);
state->isMissing = false;
}
states->InsertAt(0, state);
state->openCount++;
return state;
}
bool FileHistory::MarkFileInexistent(const WCHAR *filePath, bool hide) {
CrashIf(!filePath);
DisplayState *state = Find(filePath);
if (!state)
return false;
// move the file history entry to the end of the list
// of recently opened documents (if it exists at all),
// so that the user could still try opening it again
// and so that we don't completely forget the settings,
// should the file reappear later on
int newIdx = hide ? INT_MAX : FILE_HISTORY_MAX_RECENT - 1;
int idx = states->Find(state);
if (idx < newIdx && state != states->Last()) {
states->Remove(state);
if (states->Count() <= (size_t) newIdx)
states->Append(state);
else
states->InsertAt(newIdx, state);
}
// also delete the thumbnail and move the link towards the
// back in the Frequently Read list
delete state->thumbnail;
state->thumbnail = nullptr;
state->openCount >>= 2;
state->isMissing = hide;
return true;
}
// returns a shallow copy of the file history list, sorted
// by open count (which has a pre-multiplied recency factor)
// and with all missing states filtered out
// caller needs to delete the result (but not the contained states)
void FileHistory::GetFrequencyOrder(Vec<DisplayState *>& list) {
CrashIf(list.Count() > 0);
size_t i = 0;
for (DisplayState *ds : *states) {
ds->index = i++;
if (!ds->isMissing || ds->isPinned)
list.Append(ds);
}
list.Sort(cmpOpenCount);
}
// removes file history entries which shouldn't be saved anymore
// (see the loop below for the details)
void FileHistory::Purge(bool alwaysUseDefaultState) {
// minOpenCount is set to the number of times a file must have been
// opened to be kept (provided that there is no other valuable
// information about the file to be remembered)
int minOpenCount = 0;
if (alwaysUseDefaultState) {
Vec<DisplayState *> frequencyList;
GetFrequencyOrder(frequencyList);
if (frequencyList.Count() > FILE_HISTORY_MAX_RECENT)
minOpenCount = frequencyList.At(FILE_HISTORY_MAX_FREQUENT)->openCount / 2;
}
for (size_t j = states->Count(); j > 0; j--) {
DisplayState *state = states->At(j - 1);
// never forget pinned documents, documents we've remembered a password for and
// documents for which there are favorites
if (state->isPinned || state->decryptionKey != nullptr || state->favorites->Count() > 0)
continue;
// forget about missing documents without valuable state
if (state->isMissing && (alwaysUseDefaultState || state->useDefaultState))
states->RemoveAt(j - 1);
// forget about files last opened longer ago than the last FILE_HISTORY_MAX_FILES ones
else if (j > FILE_HISTORY_MAX_FILES)
states->RemoveAt(j - 1);
// forget about files that were hardly used (and without valuable state)
else if (alwaysUseDefaultState && state->openCount < minOpenCount && j > FILE_HISTORY_MAX_RECENT)
states->RemoveAt(j - 1);
else
continue;
DeleteDisplayState(state);
}
}