1 // Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
2 //
3 // Permission to use, copy, modify, and distribute this software for any
4 // purpose with or without fee is hereby granted, provided that the above
5 // copyright notice and this permission notice appear in all copies.
6 //
7 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10 // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12 // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13 // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 //
15 // Aegisub Project http://www.aegisub.org/
16 
17 #include <libaegisub/fs_fwd.h>
18 #include <libaegisub/signal.h>
19 
20 #include <boost/container/list.hpp>
21 #include <boost/filesystem/path.hpp>
22 #include <wx/timer.h>
23 
24 class SelectionController;
25 namespace agi { struct Context; }
26 struct AssFileCommit;
27 struct ProjectProperties;
28 
29 class SubsController {
30 	agi::Context *context;
31 	agi::signal::Connection undo_connection;
32 	agi::signal::Connection active_line_connection;
33 	agi::signal::Connection selection_connection;
34 	agi::signal::Connection text_selection_connection;
35 
36 	struct UndoInfo;
37 	boost::container::list<UndoInfo> undo_stack;
38 	boost::container::list<UndoInfo> redo_stack;
39 
40 	/// Revision counter for undo coalescing and modified state tracking
41 	int commit_id = 0;
42 	/// Last saved version of this file
43 	int saved_commit_id = 0;
44 	/// Last autosaved version of this file
45 	int autosaved_commit_id = 0;
46 
47 	/// Timer for triggering autosaves
48 	wxTimer autosave_timer;
49 
50 	/// A new file has been opened (filename)
51 	agi::signal::Signal<agi::fs::path> FileOpen;
52 	/// The file has been saved
53 	agi::signal::Signal<> FileSave;
54 
55 	/// The filename of the currently open file, if any
56 	agi::fs::path filename;
57 
58 	/// Set the filename, updating things like the MRU and last used path
59 	void SetFileName(agi::fs::path const& file);
60 
61 	void OnCommit(AssFileCommit c);
62 	void OnActiveLineChanged();
63 	void OnSelectionChanged();
64 	void OnTextSelectionChanged();
65 
66 public:
67 	SubsController(agi::Context *context);
68 	~SubsController();
69 
70 	/// Set the selection controller to use
71 	///
72 	/// Required due to that the selection controller is the subtitles grid, and
73 	/// so is created long after the subtitles controller
74 	void SetSelectionController(SelectionController *selection_controller);
75 
76 	/// The file's path and filename if any, or platform-appropriate "untitled"
77 	agi::fs::path Filename() const;
78 
79 	/// Does the file have unsaved changes?
IsModified()80 	bool IsModified() const { return commit_id != saved_commit_id; };
81 
82 	/// @brief Load from a file
83 	/// @param file File name
84 	/// @param charset Character set of file
85 	ProjectProperties Load(agi::fs::path const& file, std::string charset);
86 
87 	/// @brief Save to a file
88 	/// @param file Path to save to
89 	/// @param encoding Encoding to use, or empty to let the writer decide (which usually means "App/Save Charset")
90 	void Save(agi::fs::path const& file, std::string const& encoding="");
91 
92 	/// Close the currently open file (i.e. open a new blank file)
93 	void Close();
94 
95 	/// If there are unsaved changes, asl the user if they want to save them
96 	/// @param allow_cancel Let the user cancel the closing
97 	/// @return wxYES, wxNO or wxCANCEL (note: all three are true in a boolean context)
98 	int TryToClose(bool allow_cancel = true) const;
99 
100 	/// @brief Autosave the file if there have been any chances since the last autosave
101 	/// @return File name used or empty if no save was performed
102 	agi::fs::path AutoSave();
103 
104 	/// Can the file be saved in its current format?
105 	bool CanSave() const;
106 
107 	/// The file is about to be saved
108 	/// This signal is intended for adding metadata which is awkward or
109 	/// expensive to always keep up to date
110 	agi::signal::Signal<> UpdateProperties;
111 
112 	DEFINE_SIGNAL_ADDERS(FileOpen, AddFileOpenListener)
113 	DEFINE_SIGNAL_ADDERS(FileSave, AddFileSaveListener)
114 
115 	/// @brief Undo the last set of changes to the file
116 	void Undo();
117 	/// @brief Redo the last undone changes
118 	void Redo();
119 	/// Check if undo stack is empty
IsUndoStackEmpty()120 	bool IsUndoStackEmpty() const { return undo_stack.size() <= 1; };
121 	/// Check if redo stack is empty
IsRedoStackEmpty()122 	bool IsRedoStackEmpty() const { return redo_stack.empty(); };
123 	/// Get the description of the first undoable change
124 	wxString GetUndoDescription() const;
125 	/// Get the description of the first redoable change
126 	wxString GetRedoDescription() const;
127 };
128