1 //------------------------------------------------------------------------------
2 // emFileModel.h
3 //
4 // Copyright (C) 2005-2008,2014,2016,2018 Oliver Hamann.
5 //
6 // Homepage: http://eaglemode.sourceforge.net/
7 //
8 // This program is free software: you can redistribute it and/or modify it under
9 // the terms of the GNU General Public License version 3 as published by the
10 // Free Software Foundation.
11 //
12 // This program is distributed in the hope that it will be useful, but WITHOUT
13 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 // FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
15 // more details.
16 //
17 // You should have received a copy of the GNU General Public License version 3
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 //------------------------------------------------------------------------------
20
21 #ifndef emFileModel_h
22 #define emFileModel_h
23
24 #ifndef emPriSchedAgent_h
25 #include <emCore/emPriSchedAgent.h>
26 #endif
27
28 #ifndef emSigModel_h
29 #include <emCore/emSigModel.h>
30 #endif
31
32 class emFileModelClient;
33
34
35 //==============================================================================
36 //================================ emFileModel =================================
37 //==============================================================================
38
39 class emFileModel : public emModel {
40
41 public:
42
43 // Abstract base class for a shared file model. Such a file model can
44 // represent a file of a certain file format in memory. The most
45 // important features are:
46 //
47 // - File models can have clients (=> class emFileModelClient). The
48 // clients tell how much memory the model may allocate at most. If
49 // the model would need more, the file is not loaded. Without any
50 // client, the file is never loaded. Thus, if you want a file to be
51 // loaded by a file model, you will have to create and manage at
52 // least one emFileModelClient.
53 //
54 // - Loading and unloading is performed automatically, depending on the
55 // the state of the clients.
56 //
57 // - Saving is not performed automatically, except a derived class
58 // implements such an automatism.
59 //
60 // - Loading and saving are performed step by step in the Cycle method,
61 // so that it does not block the other engines of the program.
62 //
63 // - Normally, only one file model is loading at a time (just to avoid
64 // heavy seeking of the hard drive). The order of loading multiple
65 // file models is determined by a priority which can be set at the
66 // clients.
67
68 virtual const emString & GetFilePath() const;
69 // Path name of the file. Returns the model name by default.
70
71 const emSignal & GetFileStateSignal() const;
72 // This signal is sent on any change in the results of
73 // GetFileState(), GetMemoryNeed(), GetFileProgress() and
74 // GetErrorText().
75
76 enum FileState {
77 // Possibilities for the state of the file model.
78
79 FS_WAITING = 0,
80 // The file model wants to load the file, but it is
81 // waiting until there are no other file models in a
82 // loading state.
83
84 FS_LOADING = 1,
85 // The file model is currently reading the file.
86
87 FS_LOADED = 2,
88 // The file model is loaded.
89
90 FS_UNSAVED = 3,
91 // The file model is loaded and there are unsaved
92 // changes in the model data. This state prevents from
93 // unloading and reloading.
94
95 FS_SAVING = 4,
96 // The file model is currently writing the file.
97
98 FS_TOO_COSTLY = 5,
99 // The file model is not loaded, because there is no
100 // file model client which accepts the memory need.
101
102 FS_LOAD_ERROR = 6,
103 // The file model is not loaded, because there was an
104 // error in reading the file.
105
106 FS_SAVE_ERROR = 7,
107 // Like FS_UNSAVED, but there was an error in writing
108 // the file.
109
110 FS_MAX_VAL = 7
111 // Just the maximum possible integer value for the
112 // state.
113 };
114
115 FileState GetFileState() const;
116 // Get current state of this file model.
117
118 emUInt64 GetMemoryNeed() const;
119 // Get best known number of memory bytes, which are allocated,
120 // or which will be allocated, or which would be allocated, in
121 // loaded state. In addition to the memory need of the file
122 // model itself, this should also include the memory need of one
123 // typical client (e.g. emFilePanel), if not negligible. The
124 // result of GetMemoryNeed() is never zero. In unsaved or
125 // errored state, it could be nonsense.
126
127 double GetFileProgress() const;
128 // Get progress of loading or saving in percent.
129
130 const emString & GetErrorText() const;
131 // Get the error description when in state FS_LOAD_ERROR or
132 // FS_SAVE_ERROR.
133
134 void Update();
135 // Perform an update: If the state is FS_LOAD_ERROR, the error
136 // text is cleared and the loading will be tried again. If the
137 // state is FS_TOO_COSTLY with a last known memory need greater
138 // than one, the loading will be tried again. If the state is
139 // FS_LOADED, and if the model is out-of-date (checks file time
140 // by default), the file is unloaded for loading it again.
141 // Hints:
142 // - A good place to call this method is when creating a new
143 // emFileModelClient.
144 // - Do not call this too often, because some file models are
145 // always reloading.
146
147 static emRef<emSigModel> AcquireUpdateSignalModel(
148 emRootContext & rootContext
149 );
150 // This functions acquires a global signal model. When that
151 // signal is signaled, all file models are updated (see method
152 // Update()), except for those which have
153 // GetIgnoreUpdateSignal()==true. The signal could be used as an
154 // application-wide update signal even for reloading files which
155 // are not interfaced through emFileModel.
156
157 bool GetIgnoreUpdateSignal() const;
158 void SetIgnoreUpdateSignal(bool ignore);
159 // If true, this file model does not listen to the global update
160 // signal. Should be set at most for private file models.
161
162 void Load(bool immediately);
163 // Normally, there is no need to call this method, because
164 // loading is performed automatically. If the state is
165 // FS_WAITING, Load(false) starts the loading right away,
166 // ignoring the priority. If the state is FS_LOADING,
167 // Load(false) performs one more step in loading the file.
168 // Load(true) blocks until the state is not FS_WAITING and not
169 // FS_LOADING. For other states, this method has no effect.
170
171 void Save(bool immediately);
172 // This is similar to Load, but remember that saving is not
173 // started automatically. If the state is FS_UNSAVED,
174 // Save(false) starts the saving (which continues
175 // automatically). If the state is FS_SAVING, Save(false)
176 // performs one more step in saving the file. Save(true) blocks
177 // until the state is not FS_UNSAVED and not FS_SAVING. For
178 // other states, this method has no effect.
179
180 void ClearSaveError();
181 // Resets the state to FS_UNSAVED, if it was FS_SAVE_ERROR.
182
183 void HardResetFileState();
184 // Unload the file and restart the loading logics. This works
185 // even in unsaved or errored state, and it is the only way to
186 // take back unsaved changes.
187
188 protected:
189
190 emFileModel(emContext & context, const emString & name);
191 // Constructor.
192 // Arguments:
193 // context - Normally, this should be the root context.
194 // name - Normally, this is the path name of the file
195 // (otherwise GetFilePath has to be overloaded).
196
197 virtual ~emFileModel();
198 // Destructor.
199
200 virtual bool Cycle();
201 // See emEngine::Cycle. This one performs loading and saving.
202
203 void SetUnsavedState();
204 // This must be called before or immediately after modifying the
205 // data (except through ResetData, TryStartLoading and
206 // TryContinueLoading). Hereby, any loading or saving is
207 // aborted, and the file state is set to FS_UNSAVED. The caller
208 // should take care: if the state was FS_SAVING, the file will
209 // be corrupted, and if the state was FS_LOADING, the data may
210 // be corrupted.
211
212 virtual void ResetData() = 0;
213 // Called for unloading the file. The implementation may free
214 // allocated memory or set the data to a default state.
215
216 virtual void TryStartLoading() = 0;
217 virtual bool TryContinueLoading() = 0;
218 virtual void QuitLoading() = 0;
219 // Called for loading the file. First, TryStartLoading is
220 // called, and then TryContinueLoading is called again and again
221 // until true is returned. For aborting by an error, an
222 // exception with a user-readable error message can be thrown.
223 // Returning true from TryContinueLoading means to have finished
224 // with loading. No call should waist more than about 10
225 // milliseconds (if possible somehow). There's no problem if
226 // each call waists much fewer time, but if multiple calls
227 // cannot really continue because of waiting for a child process
228 // or so, they should do an emSleepMS(10) or something similar,
229 // otherwise we would end up in busy waiting. Before allocating
230 // a worth meaning amount of memory, a call should return and
231 // the amount of memory should be reported through the result of
232 // CalcMemoryNeed(), so that there is a chance to abort the
233 // loading and to enter the state FS_TOO_COSTLY before the
234 // memory would be allocated. Best is to determine the whole
235 // memory need in TryStartLoading (e.g. through reading just a
236 // file header), and to allocate that memory in
237 // TryContinueLoading. It is guaranteed that ResetData is called
238 // before TryStartLoading. And it is guaranteed that QuitLoading
239 // is called at the end, either on success, or for aborting, or
240 // after an error - but remember that it can never be called
241 // through the destructor of emFileModel (=> prepare the
242 // destructor of the derived class accordingly).
243
244 virtual void TryStartSaving() = 0;
245 virtual bool TryContinueSaving() = 0;
246 virtual void QuitSaving() = 0;
247 // This is just like above, but for saving. The memory need is
248 // not relevant here.
249
250 virtual emUInt64 CalcMemoryNeed() = 0;
251 // While loading, this method is called again and again to
252 // calculate the number of memory bytes, which will be allocated
253 // by this file model and by one typical file model client in
254 // loaded state. If it cannot be calculated, it should be a good
255 // approximation (e.g. from file size).
256
257 virtual double CalcFileProgress() = 0;
258 // While loading or saving, this method is called to determine
259 // the progress in percent. It's just an information for the
260 // user.
261
262 virtual void TryFetchDate();
263 // Get and remember file information for IsOutOfDate(). The default
264 // implementation is for the default implementation of IsOutOfDate().
265
266 virtual bool IsOutOfDate();
267 // Check whether the loaded file should be reloaded. The default
268 // implementation checks by file times, size and inode.
269
270 private: friend class emFileModelClient;
271
272 void ClientsChanged();
273 bool StepLoading();
274 bool StepSaving();
275 bool UpdateFileProgress();
276 void StartPSAgent();
277 void EndPSAgent();
278
279 class PSAgentClass : public emPriSchedAgent {
280 public:
281 PSAgentClass(emFileModel & fileModel);
282 protected:
283 virtual void GotAccess();
284 private:
285 emFileModel & FileModel;
286 };
287
288 friend class PSAgentClass;
289
290 emSignal FileStateSignal;
291 FileState State;
292 emUInt64 MemoryNeed;
293 double FileProgress;
294 emUInt64 FileProgressClock;
295 emString ErrorText;
296 emFileModelClient * ClientList;
297 emUInt64 MemoryLimit;
298 time_t LastMTime;
299 time_t LastCTime;
300 emUInt64 LastFSize;
301 emUInt64 LastINode;
302 PSAgentClass * PSAgent;
303 emRef<emSigModel> UpdateSignalModel; // NULL if ignored
304 };
305
GetFileStateSignal()306 inline const emSignal & emFileModel::GetFileStateSignal() const
307 {
308 return FileStateSignal;
309 }
310
GetFileState()311 inline emFileModel::FileState emFileModel::GetFileState() const
312 {
313 return State;
314 }
315
GetMemoryNeed()316 inline emUInt64 emFileModel::GetMemoryNeed() const
317 {
318 return MemoryNeed;
319 }
320
GetFileProgress()321 inline double emFileModel::GetFileProgress() const
322 {
323 return FileProgress;
324 }
325
GetErrorText()326 inline const emString & emFileModel::GetErrorText() const
327 {
328 return ErrorText;
329 }
330
GetIgnoreUpdateSignal()331 inline bool emFileModel::GetIgnoreUpdateSignal() const
332 {
333 return UpdateSignalModel==NULL;
334 }
335
336
337 //==============================================================================
338 //============================= emFileModelClient ==============================
339 //==============================================================================
340
341 class emFileModelClient : public emUncopyable {
342
343 public:
344
345 // Class for a client on an emFileModel. Multiple clients can connect to
346 // the same file model. And:
347 //
348 // - Each client tells about the maximum memory which may be allocated
349 // by the file model and one typical client (e.g. emFilePanel). If no
350 // client accepts the memory need this way, the file is not loaded or
351 // it is unloaded.
352 //
353 // - Each client tells about the priority which is used for determining
354 // the order of loading file models. The maximum priority of all
355 // clients is taken.
356 //
357 // On each modification of a memory limit or a priority, the logics of
358 // emFileModel reacts automatically.
359 //
360 // Hint: Even have a look at the class emFilePanel. It's a base class
361 // for panels which want to be file mode clients.
362
363 emFileModelClient(emFileModel * model=NULL, emUInt64 memoryLimit=0,
364 double priority=0.0);
365 // Constructor.
366 // Arguments:
367 // model - See SetModel below.
368 // memoryLimit - See SetMemoryLimit below.
369 // priority - See SetPriority below.
370
371 virtual ~emFileModelClient();
372 // Destructor.
373
374 emFileModel * GetModel() const;
375 void SetModel(emFileModel * model=NULL);
376 // The file model this client is connected to. NULL means to
377 // have disconnected state.
378
379 void SetMemoryLimit(emUInt64 bytes);
380 emUInt64 GetMemoryLimit() const;
381 // Maximum memory need accepted for loading the file, from sight
382 // of this client. Usually, this should be set from
383 // emPanel::GetMemoryLimit().
384
385 double GetPriority() const;
386 void SetPriority(double priority);
387 // Priority in loading the file, from sight of this client.
388 // Usually, this should be set from
389 // emPanel::GetUpdatePriority().
390
391 private: friend class emFileModel;
392
393 emRef<emFileModel> Model;
394 emUInt64 MemoryLimit;
395 double Priority;
396 emFileModelClient * * ThisPtrInList;
397 emFileModelClient * NextInList;
398 };
399
GetModel()400 inline emFileModel * emFileModelClient::GetModel() const
401 {
402 return Model;
403 }
404
GetMemoryLimit()405 inline emUInt64 emFileModelClient::GetMemoryLimit() const
406 {
407 return MemoryLimit;
408 }
409
GetPriority()410 inline double emFileModelClient::GetPriority() const
411 {
412 return Priority;
413 }
414
415
416 #endif
417