Merge lp:~artem-anufrij/scratch/dont-force-to-save-unsaved into lp:~elementary-apps/scratch/scratch
- dont-force-to-save-unsaved
- Merge into scratch
Status: | Merged |
---|---|
Approved by: | Robert Roth |
Approved revision: | 1414 |
Merged at revision: | 1393 |
Proposed branch: | lp:~artem-anufrij/scratch/dont-force-to-save-unsaved |
Merge into: | lp:~elementary-apps/scratch/scratch |
Diff against target: |
746 lines (+236/-186) 7 files modified
HACKING (+1/-1) src/Dialogs/PreferencesDialog.vala (+1/-5) src/MainWindow.vala (+53/-6) src/Scratch.vala (+16/-2) src/Services/Document.vala (+145/-161) src/Widgets/DocumentView.vala (+13/-3) src/Widgets/ToolBar.vala (+7/-8) |
To merge this branch: | bzr merge lp:~artem-anufrij/scratch/dont-force-to-save-unsaved |
Related bugs: | |
Related blueprints: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Robert Roth (community) | code review | Approve | |
Danielle Foré | ux | Approve | |
Review via email: mp+237154@code.launchpad.net |
Commit message
Use temporary files for unsaved files to be able to restore them at next startup
Description of the change
BLUEPRINT DESCRIPTION:
+1 for keeping in .local/
Artem Anufrij (artem-anufrij) wrote : | # |
Please check the fixes.
- temporary location: .local/
- text style was fixed.
Robert Roth (evfool) wrote : | # |
See my inline comments. I'm still not sure the scratch app instance should be made singleton just to be able to access the path variable, that's a second reason why settings-based path was suggested, as the settings object is available globally.
- 1405. By artem-anufrij
-
code fixing
Robert Roth (evfool) wrote : | # |
Ok, looks nice, as per Slack comments the path doesn't have to be configurable, so we're keeping the app instance singleton. Approved.
- 1406. By artem-anufrij
-
temporary typer
Danielle Foré (danrabbit) wrote : | # |
I think if you close the individual tab (as opposed to the whole app) you should be prompted to either save or delete. I don't think we should encourage a workflow where users can perpetually store a bunch of documents in a hidden folder. Additionally, in the current workflow I'm not sure how I'm supposed to discard a document that I've decided I don't actually want to save.
I'm also thinking that we should override the tab/title name for unsaved documents and just call them "unsaved document" or something instead of referring to them as a file name with a location inside the app.
Artem Anufrij (artem-anufrij) wrote : | # |
I have changed the functionality as agreed. Please check this out.
Robert Roth (evfool) wrote : | # |
Code style is mostly ok, see one minor request inline.
- 1410. By artem-anufrij
-
Code style has been improved.
Robert Roth (evfool) wrote : | # |
Code style fixed, approving, waiting for Dan's approval.
Danielle Foré (danrabbit) wrote : | # |
Maybe I'm missing something, but the feature seems to no longer work at all. Trying to close the app with an unsaved document prompts me to save.
Also the window title still reflects the name and location of the file
- 1411. By artem-anufrij
-
Window title fixed
App closing: save 'unsaved' files in ./local/share/... without save dialog
Tab closing: show save dialog for 'unsaved' files
Artem Anufrij (artem-anufrij) wrote : | # |
Hey Dan,
sorry I have overlooked the: "...(as opposed to the whole app)...".
Now it feels smooth.
Danielle Foré (danrabbit) wrote : | # |
Works as expected now. Thanks Artem! This is great :)
Robert Roth (evfool) wrote : | # |
Looks fine, two more indentation issues to fix before the final approval.
- 1414. By artem-anufrij
-
code style fixed
Artem Anufrij (artem-anufrij) wrote : | # |
@Robert,
line 155: this is only code style:
https:/
line 292: fixed
Robert Roth (evfool) wrote : | # |
Ok, Approving. Thanks for the several fixes and your perseverence, it is something of great value.
Preview Diff
1 | === modified file 'HACKING' | |||
2 | --- HACKING 2013-08-17 21:53:30 +0000 | |||
3 | +++ HACKING 2014-10-18 19:50:44 +0000 | |||
4 | @@ -152,4 +152,4 @@ | |||
5 | 152 | bzr branch lp:scratch scratch | 152 | bzr branch lp:scratch scratch |
6 | 153 | bzr branch scratch/ scratch-fix-123456 | 153 | bzr branch scratch/ scratch-fix-123456 |
7 | 154 | cd scratch-fix-123456 | 154 | cd scratch-fix-123456 |
9 | 155 | bzr pull lp:scratch | 155 | bzr pull lp:scratch |
10 | 156 | \ No newline at end of file | 156 | \ No newline at end of file |
11 | 157 | 157 | ||
12 | === modified file 'src/Dialogs/PreferencesDialog.vala' | |||
13 | --- src/Dialogs/PreferencesDialog.vala 2014-08-07 09:22:40 +0000 | |||
14 | +++ src/Dialogs/PreferencesDialog.vala 2014-10-18 19:50:44 +0000 | |||
15 | @@ -288,10 +288,6 @@ | |||
16 | 288 | var scheme = scheme_manager.get_scheme (scheme_id); | 288 | var scheme = scheme_manager.get_scheme (scheme_id); |
17 | 289 | style_scheme.append (scheme.id, scheme.name); | 289 | style_scheme.append (scheme.id, scheme.name); |
18 | 290 | } | 290 | } |
19 | 291 | |||
20 | 292 | |||
21 | 293 | } | 291 | } |
22 | 294 | |||
23 | 295 | } | 292 | } |
26 | 296 | 293 | } // Namespace | |
25 | 297 | } // Namespace | ||
27 | 298 | \ No newline at end of file | 294 | \ No newline at end of file |
28 | 299 | 295 | ||
29 | === modified file 'src/MainWindow.vala' | |||
30 | --- src/MainWindow.vala 2014-10-05 20:56:25 +0000 | |||
31 | +++ src/MainWindow.vala 2014-10-18 19:50:44 +0000 | |||
32 | @@ -86,6 +86,9 @@ | |||
33 | 86 | // Restore session | 86 | // Restore session |
34 | 87 | restore_saved_state_extra (); | 87 | restore_saved_state_extra (); |
35 | 88 | 88 | ||
36 | 89 | // Crate folder for unsaved documents | ||
37 | 90 | create_unsaved_documents_directory (); | ||
38 | 91 | |||
39 | 89 | #if HAVE_ZEITGEIST | 92 | #if HAVE_ZEITGEIST |
40 | 90 | // Set up the Data Source Registry for Zeitgeist | 93 | // Set up the Data Source Registry for Zeitgeist |
41 | 91 | registry = new DataSourceRegistry (); | 94 | registry = new DataSourceRegistry (); |
42 | @@ -181,7 +184,12 @@ | |||
43 | 181 | path = _("Trash"); | 184 | path = _("Trash"); |
44 | 182 | 185 | ||
45 | 183 | path = Uri.unescape_string (path); | 186 | path = Uri.unescape_string (path); |
47 | 184 | this.toolbar.title = doc.file.get_basename () + " (%s)".printf(path); | 187 | |
48 | 188 | string toolbar_title = doc.file.get_basename () + " (%s)".printf (path); | ||
49 | 189 | if (doc.is_file_temporary) | ||
50 | 190 | toolbar_title = "(%s)".printf (doc.get_basename ()); | ||
51 | 191 | |||
52 | 192 | this.toolbar.title = toolbar_title; | ||
53 | 185 | } | 193 | } |
54 | 186 | else { | 194 | else { |
55 | 187 | this.toolbar.title = this.app.app_cmd_name; | 195 | this.toolbar.title = this.app.app_cmd_name; |
56 | @@ -241,14 +249,17 @@ | |||
57 | 241 | 249 | ||
58 | 242 | this.search_revealer.set_reveal_child (false); | 250 | this.search_revealer.set_reveal_child (false); |
59 | 243 | 251 | ||
60 | 252 | main_actions.get_action ("OpenTemporaryFiles").visible = this.has_temporary_files (); | ||
61 | 244 | main_actions.get_action ("SaveFile").visible = !settings.autosave; | 253 | main_actions.get_action ("SaveFile").visible = !settings.autosave; |
62 | 245 | main_actions.get_action ("Templates").visible = plugins.plugin_iface.template_manager.template_available; | 254 | main_actions.get_action ("Templates").visible = plugins.plugin_iface.template_manager.template_available; |
63 | 246 | plugins.plugin_iface.template_manager.notify["template_available"].connect ( () => { | 255 | plugins.plugin_iface.template_manager.notify["template_available"].connect ( () => { |
64 | 247 | main_actions.get_action ("Templates").visible = plugins.plugin_iface.template_manager.template_available; | 256 | main_actions.get_action ("Templates").visible = plugins.plugin_iface.template_manager.template_available; |
65 | 248 | }); | 257 | }); |
66 | 249 | 258 | ||
69 | 250 | // Show welcome by default | 259 | if (has_temporary_files ()) |
70 | 251 | this.split_view.show_welcome (); | 260 | action_open_temporary_files (); |
71 | 261 | else | ||
72 | 262 | this.split_view.show_welcome (); | ||
73 | 252 | 263 | ||
74 | 253 | // Plugins hook | 264 | // Plugins hook |
75 | 254 | HookFunc hook_func = () => { | 265 | HookFunc hook_func = () => { |
76 | @@ -268,7 +279,6 @@ | |||
77 | 268 | hook_func (); | 279 | hook_func (); |
78 | 269 | }); | 280 | }); |
79 | 270 | hook_func (); | 281 | hook_func (); |
80 | 271 | |||
81 | 272 | } | 282 | } |
82 | 273 | 283 | ||
83 | 274 | private void on_plugin_toggled (Gtk.Notebook notebook) { | 284 | private void on_plugin_toggled (Gtk.Notebook notebook) { |
84 | @@ -381,13 +391,25 @@ | |||
85 | 381 | return split_view.is_empty (); | 391 | return split_view.is_empty (); |
86 | 382 | } | 392 | } |
87 | 383 | 393 | ||
88 | 394 | public bool has_temporary_files () { | ||
89 | 395 | FileEnumerator enumerator = File.new_for_path (app.data_home_folder_unsaved).enumerate_children (FILE_ATTRIBUTE_STANDARD_NAME, 0, null); | ||
90 | 396 | var fileinfo = enumerator.next_file (null); | ||
91 | 397 | while (fileinfo != null) { | ||
92 | 398 | if (!fileinfo.get_name ().has_suffix ("~")) { | ||
93 | 399 | return true; | ||
94 | 400 | } | ||
95 | 401 | fileinfo = enumerator.next_file (null); | ||
96 | 402 | } | ||
97 | 403 | return false; | ||
98 | 404 | } | ||
99 | 405 | |||
100 | 384 | // Check if there no unsaved changes | 406 | // Check if there no unsaved changes |
101 | 385 | private bool check_unsaved_changes () { | 407 | private bool check_unsaved_changes () { |
102 | 386 | if (!is_empty ()) { | 408 | if (!is_empty ()) { |
103 | 387 | foreach (var w in this.split_view.views) { | 409 | foreach (var w in this.split_view.views) { |
104 | 388 | var view = w as Scratch.Widgets.DocumentView; | 410 | var view = w as Scratch.Widgets.DocumentView; |
105 | 389 | foreach (var doc in view.docs) { | 411 | foreach (var doc in view.docs) { |
107 | 390 | if (!doc.close ()) { | 412 | if (!doc.close (true)) { |
108 | 391 | view.set_current_document (doc); | 413 | view.set_current_document (doc); |
109 | 392 | return false; | 414 | return false; |
110 | 393 | } | 415 | } |
111 | @@ -418,6 +440,16 @@ | |||
112 | 418 | vp.set_position (Scratch.saved_state.vp_size); | 440 | vp.set_position (Scratch.saved_state.vp_size); |
113 | 419 | } | 441 | } |
114 | 420 | 442 | ||
115 | 443 | private void create_unsaved_documents_directory () { | ||
116 | 444 | File directory = File.new_for_path (app.data_home_folder_unsaved); | ||
117 | 445 | if (!directory.query_exists ()) { | ||
118 | 446 | debug ("create 'unsaved' directory: %s", directory.get_path ()); | ||
119 | 447 | directory.make_directory_with_parents (); | ||
120 | 448 | return; | ||
121 | 449 | } | ||
122 | 450 | debug ("'unsaved' directory already exists."); | ||
123 | 451 | } | ||
124 | 452 | |||
125 | 421 | private void update_saved_state () { | 453 | private void update_saved_state () { |
126 | 422 | 454 | ||
127 | 423 | // Save window state | 455 | // Save window state |
128 | @@ -517,6 +549,21 @@ | |||
129 | 517 | filech.close (); | 549 | filech.close (); |
130 | 518 | } | 550 | } |
131 | 519 | 551 | ||
132 | 552 | void action_open_temporary_files () { | ||
133 | 553 | FileEnumerator enumerator = File.new_for_path (app.data_home_folder_unsaved).enumerate_children (FILE_ATTRIBUTE_STANDARD_NAME, 0, null); | ||
134 | 554 | var fileinfo = enumerator.next_file (null); | ||
135 | 555 | while (fileinfo != null) { | ||
136 | 556 | if (!fileinfo.get_name ().has_suffix ("~")) { | ||
137 | 557 | debug ("open temporary file: %s", fileinfo.get_name ()); | ||
138 | 558 | var file = File.new_for_path (app.data_home_folder_unsaved + fileinfo.get_name ()); | ||
139 | 559 | var doc = new Scratch.Services.Document (this.main_actions, file); | ||
140 | 560 | this.open_document (doc); | ||
141 | 561 | } | ||
142 | 562 | // Next file info | ||
143 | 563 | fileinfo = enumerator.next_file (null); | ||
144 | 564 | } | ||
145 | 565 | } | ||
146 | 566 | |||
147 | 520 | void action_save () { | 567 | void action_save () { |
148 | 521 | this.get_current_document ().save (); | 568 | this.get_current_document ().save (); |
149 | 522 | } | 569 | } |
150 | @@ -694,7 +741,7 @@ | |||
151 | 694 | /* label, accelerator */ N_("Redo"), "<Control><shift>z", | 741 | /* label, accelerator */ N_("Redo"), "<Control><shift>z", |
152 | 695 | /* tooltip */ N_("Redo the last undone action"), | 742 | /* tooltip */ N_("Redo the last undone action"), |
153 | 696 | action_redo }, | 743 | action_redo }, |
155 | 697 | { "Revert", Gtk.Stock.REVERT_TO_SAVED, | 744 | { "Revert", Gtk.Stock.REVERT_TO_SAVED, |
156 | 698 | /* label, accelerator */ N_("Revert"), "<Control><shift>o", | 745 | /* label, accelerator */ N_("Revert"), "<Control><shift>o", |
157 | 699 | /* tooltip */ N_("Restore this file"), | 746 | /* tooltip */ N_("Restore this file"), |
158 | 700 | action_revert }, | 747 | action_revert }, |
159 | 701 | 748 | ||
160 | === modified file 'src/Scratch.vala' | |||
161 | --- src/Scratch.vala 2014-10-05 21:21:40 +0000 | |||
162 | +++ src/Scratch.vala 2014-10-18 19:50:44 +0000 | |||
163 | @@ -34,7 +34,9 @@ | |||
164 | 34 | private GLib.List <MainWindow> windows; | 34 | private GLib.List <MainWindow> windows; |
165 | 35 | 35 | ||
166 | 36 | public string app_cmd_name { get { return _app_cmd_name; } } | 36 | public string app_cmd_name { get { return _app_cmd_name; } } |
167 | 37 | public string data_home_folder_unsaved { get { return _data_home_folder_unsaved; } } | ||
168 | 37 | private static string _app_cmd_name; | 38 | private static string _app_cmd_name; |
169 | 39 | private static string _data_home_folder_unsaved; | ||
170 | 38 | private static string _cwd; | 40 | private static string _cwd; |
171 | 39 | private static bool print_version = false; | 41 | private static bool print_version = false; |
172 | 40 | private static bool create_new_tab = false; | 42 | private static bool create_new_tab = false; |
173 | @@ -90,6 +92,18 @@ | |||
174 | 90 | services = new ServicesSettings (); | 92 | services = new ServicesSettings (); |
175 | 91 | windows = new GLib.List <MainWindow> (); | 93 | windows = new GLib.List <MainWindow> (); |
176 | 92 | 94 | ||
177 | 95 | // Init data home folder for unsaved text files | ||
178 | 96 | _data_home_folder_unsaved = Environment.get_user_data_dir () + "/" + exec_name + "/unsaved/"; | ||
179 | 97 | } | ||
180 | 98 | |||
181 | 99 | public static ScratchApp _instance = null; | ||
182 | 100 | |||
183 | 101 | public static ScratchApp instance { | ||
184 | 102 | get { | ||
185 | 103 | if (_instance == null) | ||
186 | 104 | _instance = new ScratchApp (); | ||
187 | 105 | return _instance; | ||
188 | 106 | } | ||
189 | 93 | } | 107 | } |
190 | 94 | 108 | ||
191 | 95 | protected override int command_line (ApplicationCommandLine command_line) { | 109 | protected override int command_line (ApplicationCommandLine command_line) { |
192 | @@ -253,8 +267,8 @@ | |||
193 | 253 | return Posix.EXIT_SUCCESS; | 267 | return Posix.EXIT_SUCCESS; |
194 | 254 | } | 268 | } |
195 | 255 | 269 | ||
197 | 256 | var app = new ScratchApp (); | 270 | ScratchApp app = ScratchApp.instance; |
198 | 257 | return app.run (args_primary_instance); | 271 | return app.run (args_primary_instance); |
199 | 258 | } | 272 | } |
200 | 259 | } | 273 | } |
202 | 260 | } | 274 | } |
203 | 261 | \ No newline at end of file | 275 | \ No newline at end of file |
204 | 262 | 276 | ||
205 | === modified file 'src/Services/Document.vala' | |||
206 | --- src/Services/Document.vala 2014-08-06 05:45:44 +0000 | |||
207 | +++ src/Services/Document.vala 2014-10-18 19:50:44 +0000 | |||
208 | @@ -56,15 +56,23 @@ | |||
209 | 56 | private Gtk.InfoBar info_bar = new Gtk.InfoBar (); | 56 | private Gtk.InfoBar info_bar = new Gtk.InfoBar (); |
210 | 57 | 57 | ||
211 | 58 | // Objects | 58 | // Objects |
214 | 59 | private File? _file = null; | 59 | private File _file = null; |
215 | 60 | public File? file { | 60 | public File file { |
216 | 61 | get { return _file; } | 61 | get { return _file; } |
218 | 62 | set { _file = value; file_changed (); } | 62 | set { |
219 | 63 | _file = value; | ||
220 | 64 | file_changed (); | ||
221 | 65 | } | ||
222 | 63 | } | 66 | } |
223 | 64 | public string original_content; | 67 | public string original_content; |
224 | 65 | public string? last_saved_content = null; | 68 | public string? last_saved_content = null; |
225 | 66 | public bool saved = true; | 69 | public bool saved = true; |
226 | 67 | private bool error_shown = false; | 70 | private bool error_shown = false; |
227 | 71 | public bool is_file_temporary { | ||
228 | 72 | get { | ||
229 | 73 | return file.get_path ().has_prefix (ScratchApp.instance.data_home_folder_unsaved); | ||
230 | 74 | } | ||
231 | 75 | } | ||
232 | 68 | 76 | ||
233 | 69 | // It is used to load file content on focusing | 77 | // It is used to load file content on focusing |
234 | 70 | private bool loaded = false; | 78 | private bool loaded = false; |
235 | @@ -134,43 +142,6 @@ | |||
236 | 134 | 142 | ||
237 | 135 | public async bool open () { | 143 | public async bool open () { |
238 | 136 | this.source_view.buffer.create_tag ("highlight_search_all", "background", "yellow", null); | 144 | this.source_view.buffer.create_tag ("highlight_search_all", "background", "yellow", null); |
239 | 137 | if (file == null) { | ||
240 | 138 | message ("New Document opened"); | ||
241 | 139 | this.source_view.focus_in_event.connect (() => { | ||
242 | 140 | main_actions.get_action ("SaveFile").visible = true; | ||
243 | 141 | check_file_status (); | ||
244 | 142 | check_undoable_actions (); | ||
245 | 143 | return false; | ||
246 | 144 | }); | ||
247 | 145 | uint timeout_saving = -1; | ||
248 | 146 | this.source_view.buffer.changed.connect (() => { | ||
249 | 147 | check_undoable_actions (); | ||
250 | 148 | // If it wasn't yet saved | ||
251 | 149 | if (file == null) { | ||
252 | 150 | this.set_saved_status (false); | ||
253 | 151 | return; | ||
254 | 152 | } | ||
255 | 153 | // Save if autosave is ON | ||
256 | 154 | if (settings.autosave) { | ||
257 | 155 | if (timeout_saving >= 0) { | ||
258 | 156 | Source.remove (timeout_saving); | ||
259 | 157 | timeout_saving = -1; | ||
260 | 158 | } | ||
261 | 159 | timeout_saving = Timeout.add (3000, () => { | ||
262 | 160 | save (); | ||
263 | 161 | timeout_saving = -1; | ||
264 | 162 | return false; | ||
265 | 163 | }); | ||
266 | 164 | } | ||
267 | 165 | else if (!settings.autosave || file == null) | ||
268 | 166 | this.set_saved_status (false); | ||
269 | 167 | }); | ||
270 | 168 | this.set_saved_status (true); | ||
271 | 169 | this.has_started_loading = true; | ||
272 | 170 | this.loaded = true; | ||
273 | 171 | |||
274 | 172 | return true; | ||
275 | 173 | } | ||
276 | 174 | 145 | ||
277 | 175 | // If it does not exists, let's create it! | 146 | // If it does not exists, let's create it! |
278 | 176 | if (!exists ()) { | 147 | if (!exists ()) { |
279 | @@ -227,14 +198,17 @@ | |||
280 | 227 | return true; | 198 | return true; |
281 | 228 | } | 199 | } |
282 | 229 | 200 | ||
284 | 230 | public new bool close () { | 201 | public new bool close (bool app_closing = false) { |
285 | 231 | 202 | ||
286 | 232 | message ("Closing \"%s\"", get_basename ()); | 203 | message ("Closing \"%s\"", get_basename ()); |
287 | 233 | 204 | ||
288 | 234 | bool ret_value = true; | 205 | bool ret_value = true; |
290 | 235 | 206 | if (app_closing && is_file_temporary && !delete_temporary_file ()) { | |
291 | 207 | debug ("Save temporary file!"); | ||
292 | 208 | this.save (); | ||
293 | 209 | } | ||
294 | 236 | // Check for unsaved changes | 210 | // Check for unsaved changes |
296 | 237 | if (!this.saved) { | 211 | else if (!this.saved || (!app_closing && is_file_temporary && !delete_temporary_file ())) { |
297 | 238 | debug ("There are unsaved changes, showing a Message Dialog!"); | 212 | debug ("There are unsaved changes, showing a Message Dialog!"); |
298 | 239 | 213 | ||
299 | 240 | // Create a GtkDialog | 214 | // Create a GtkDialog |
300 | @@ -265,42 +239,40 @@ | |||
301 | 265 | ret_value = false; | 239 | ret_value = false; |
302 | 266 | break; | 240 | break; |
303 | 267 | case Gtk.ResponseType.YES: | 241 | case Gtk.ResponseType.YES: |
306 | 268 | this.save (); | 242 | if (this.is_file_temporary) |
307 | 269 | ret_value = true; | 243 | this.save_as (); |
308 | 244 | else | ||
309 | 245 | this.save (); | ||
310 | 270 | break; | 246 | break; |
311 | 271 | case Gtk.ResponseType.NO: | 247 | case Gtk.ResponseType.NO: |
313 | 272 | ret_value = true; | 248 | if (this.is_file_temporary) |
314 | 249 | delete_temporary_file (true); | ||
315 | 273 | break; | 250 | break; |
316 | 274 | } | 251 | } |
317 | 275 | dialog.destroy (); | 252 | dialog.destroy (); |
319 | 276 | } | 253 | } |
320 | 277 | 254 | ||
323 | 278 | if (file != null && ret_value) { | 255 | if (ret_value) { |
324 | 279 | // Delete backup copy file | 256 | // Delete backup copy file |
325 | 280 | delete_backup (); | 257 | delete_backup (); |
326 | 258 | } | ||
327 | 281 | #if HAVE_ZEITGEIST | 259 | #if HAVE_ZEITGEIST |
330 | 282 | // Zeitgeist integration | 260 | // Zeitgeist integration |
331 | 283 | zg_log.close_insert (file.get_uri (), get_mime_type ()); | 261 | zg_log.close_insert (file.get_uri (), get_mime_type ()); |
332 | 284 | #endif | 262 | #endif |
333 | 285 | } | ||
334 | 286 | 263 | ||
335 | 287 | return ret_value; | 264 | return ret_value; |
336 | 288 | } | 265 | } |
337 | 289 | 266 | ||
338 | 290 | public bool save () { | 267 | public bool save () { |
340 | 291 | if (last_saved_content == get_text () && this.file != null) | 268 | if (last_saved_content == get_text ()) |
341 | 292 | return false; | 269 | return false; |
342 | 293 | 270 | ||
343 | 294 | if (!this.loaded) | 271 | if (!this.loaded) |
344 | 295 | return false; | 272 | return false; |
345 | 296 | 273 | ||
353 | 297 | // Create backup copy file if it does not still exist | 274 | // Create backup copy file |
354 | 298 | if (this.file != null) | 275 | this.create_backup (); |
348 | 299 | create_backup (); | ||
349 | 300 | |||
350 | 301 | // Show save as dialog if file is null | ||
351 | 302 | if (this.file == null) | ||
352 | 303 | return this.save_as (); | ||
355 | 304 | 276 | ||
356 | 305 | // Replace old content with the new one | 277 | // Replace old content with the new one |
357 | 306 | try { | 278 | try { |
358 | @@ -308,6 +280,7 @@ | |||
359 | 308 | file.replace_contents (this.source_view.buffer.text.data, null, false, 0, out s); | 280 | file.replace_contents (this.source_view.buffer.text.data, null, false, 0, out s); |
360 | 309 | } catch (Error e) { | 281 | } catch (Error e) { |
361 | 310 | warning ("Cannot save \"%s\": %s", get_basename (), e.message); | 282 | warning ("Cannot save \"%s\": %s", get_basename (), e.message); |
362 | 283 | return false; | ||
363 | 311 | } | 284 | } |
364 | 312 | 285 | ||
365 | 313 | #if HAVE_ZEITGEIST | 286 | #if HAVE_ZEITGEIST |
366 | @@ -330,6 +303,9 @@ | |||
367 | 330 | // New file | 303 | // New file |
368 | 331 | var filech = Utils.new_file_chooser_dialog (Gtk.FileChooserAction.SAVE, _("Save File"), null); | 304 | var filech = Utils.new_file_chooser_dialog (Gtk.FileChooserAction.SAVE, _("Save File"), null); |
369 | 332 | 305 | ||
370 | 306 | string current_file = file.get_path (); | ||
371 | 307 | bool is_current_file_temporary = this.is_file_temporary; | ||
372 | 308 | |||
373 | 333 | if (filech.run () == Gtk.ResponseType.ACCEPT) { | 309 | if (filech.run () == Gtk.ResponseType.ACCEPT) { |
374 | 334 | this.file = File.new_for_uri (filech.get_file ().get_uri ()); | 310 | this.file = File.new_for_uri (filech.get_file ().get_uri ()); |
375 | 335 | // Update last visited path | 311 | // Update last visited path |
376 | @@ -341,10 +317,20 @@ | |||
377 | 341 | return false; | 317 | return false; |
378 | 342 | } | 318 | } |
379 | 343 | 319 | ||
381 | 344 | // reset the last saved content | 320 | // Reset the last saved content |
382 | 345 | last_saved_content = null; | 321 | last_saved_content = null; |
383 | 346 | 322 | ||
385 | 347 | save (); | 323 | if (save () && is_current_file_temporary) { |
386 | 324 | try { | ||
387 | 325 | // Delete temporary file | ||
388 | 326 | File.new_for_path (current_file).delete (); | ||
389 | 327 | } catch (Error err) { | ||
390 | 328 | message ("Temporary file cannot be deleted: %s", current_file); | ||
391 | 329 | } | ||
392 | 330 | } | ||
393 | 331 | |||
394 | 332 | // Delete backup file | ||
395 | 333 | delete_backup (current_file + "~"); | ||
396 | 348 | 334 | ||
397 | 349 | // Change syntax highlight | 335 | // Change syntax highlight |
398 | 350 | this.source_view.change_syntax_highlight_from_file (this.file); | 336 | this.source_view.change_syntax_highlight_from_file (this.file); |
399 | @@ -369,19 +355,15 @@ | |||
400 | 369 | 355 | ||
401 | 370 | // Get mime type for the document | 356 | // Get mime type for the document |
402 | 371 | public string get_mime_type () { | 357 | public string get_mime_type () { |
416 | 372 | if (file == null) | 358 | FileInfo info; |
417 | 373 | return "text/plain"; | 359 | string mime_type; |
418 | 374 | else { | 360 | try { |
419 | 375 | FileInfo info; | 361 | info = file.query_info ("standard::*", FileQueryInfoFlags.NONE, null); |
420 | 376 | string mime_type; | 362 | mime_type = ContentType.get_mime_type (info.get_attribute_as_string (FileAttribute.STANDARD_CONTENT_TYPE)); |
421 | 377 | try { | 363 | return mime_type; |
422 | 378 | info = file.query_info ("standard::*", FileQueryInfoFlags.NONE, null); | 364 | } catch (Error e) { |
423 | 379 | mime_type = ContentType.get_mime_type (info.get_attribute_as_string (FileAttribute.STANDARD_CONTENT_TYPE)); | 365 | warning ("%s", e.message); |
424 | 380 | return mime_type; | 366 | return "undefined"; |
412 | 381 | } catch (Error e) { | ||
413 | 382 | warning ("%s", e.message); | ||
414 | 383 | return "undefined"; | ||
415 | 384 | } | ||
425 | 385 | } | 367 | } |
426 | 386 | } | 368 | } |
427 | 387 | 369 | ||
428 | @@ -406,17 +388,15 @@ | |||
429 | 406 | 388 | ||
430 | 407 | // Get file uri | 389 | // Get file uri |
431 | 408 | public string get_uri () { | 390 | public string get_uri () { |
432 | 409 | if (file == null) | ||
433 | 410 | return ""; | ||
434 | 411 | return this.file.get_uri (); | 391 | return this.file.get_uri (); |
436 | 412 | } | 392 | } |
437 | 413 | 393 | ||
438 | 414 | // Get file name | 394 | // Get file name |
439 | 415 | public string get_basename () { | 395 | public string get_basename () { |
441 | 416 | if (file != null) | 396 | if (is_file_temporary) |
442 | 397 | return _("New Document"); | ||
443 | 398 | else | ||
444 | 417 | return file.get_basename (); | 399 | return file.get_basename (); |
445 | 418 | else | ||
446 | 419 | return _("New Document"); | ||
447 | 420 | } | 400 | } |
448 | 421 | 401 | ||
449 | 422 | // Set InfoBars message | 402 | // Set InfoBars message |
450 | @@ -553,7 +533,7 @@ | |||
451 | 553 | if (this.error_shown) | 533 | if (this.error_shown) |
452 | 554 | return; | 534 | return; |
453 | 555 | this.error_shown = true; | 535 | this.error_shown = true; |
455 | 556 | string message = _("File \"<b>%s</b>\" cannot be read. Maybe it is corrupt\nor you do not have the necessary permissions to read it.").printf (get_basename ()); | 536 | string message = _("File \"%s\" cannot be read. Maybe it is corrupt\nor you do not have the necessary permissions to read it.").printf ("<b>%s</b>".printf (get_basename ())); |
456 | 557 | var parent_window = source_view.get_toplevel () as Gtk.Window; | 537 | var parent_window = source_view.get_toplevel () as Gtk.Window; |
457 | 558 | var dialog = new Gtk.MessageDialog.with_markup (parent_window, Gtk.DialogFlags.MODAL, | 538 | var dialog = new Gtk.MessageDialog.with_markup (parent_window, Gtk.DialogFlags.MODAL, |
458 | 559 | Gtk.MessageType.ERROR, | 539 | Gtk.MessageType.ERROR, |
459 | @@ -566,84 +546,73 @@ | |||
460 | 566 | 546 | ||
461 | 567 | // Check if the file was deleted/changed by an external source | 547 | // Check if the file was deleted/changed by an external source |
462 | 568 | public void check_file_status () { | 548 | public void check_file_status () { |
493 | 569 | if (file != null) { | 549 | // If the file does not exist anymore |
494 | 570 | // If the file does not exist anymore | 550 | if (!exists ()) { |
495 | 571 | if (!exists ()) { | 551 | if (mounted == false) { |
496 | 572 | if (mounted == false) { | 552 | string message = _("The location containing the file \"%s\" was unmounted. Do you want to save somewhere else?").printf ("<b>%s</b>".printf (get_basename ())); |
497 | 573 | string message = _("The location containing the file") + " \"<b>%s</b>\" ".printf (get_basename ()) + | 553 | |
498 | 574 | _("was unmounted. Do you want to save somewhere else?"); | 554 | set_message (Gtk.MessageType.WARNING, message, _("Save As…"), () => { |
469 | 575 | |||
470 | 576 | set_message (Gtk.MessageType.WARNING, message, _("Save As…"), () => { | ||
471 | 577 | this.save_as (); | ||
472 | 578 | hide_info_bar (); | ||
473 | 579 | }); | ||
474 | 580 | } else { | ||
475 | 581 | string message = _("File") + " \"<b>%s</b>\" ".printf (get_basename ()) + | ||
476 | 582 | _("was deleted. Do you want to save it anyway?"); | ||
477 | 583 | |||
478 | 584 | set_message (Gtk.MessageType.WARNING, message, _("Save"), () => { | ||
479 | 585 | this.save (); | ||
480 | 586 | hide_info_bar (); | ||
481 | 587 | }); | ||
482 | 588 | } | ||
483 | 589 | main_actions.get_action ("SaveFile").sensitive = false; | ||
484 | 590 | this.source_view.editable = false; | ||
485 | 591 | return; | ||
486 | 592 | } | ||
487 | 593 | // If the file can't be written | ||
488 | 594 | if (!can_write ()) { | ||
489 | 595 | string message = _("You cannot save changes on file") + " \"<b>%s</b>\". ".printf (get_basename ()) + | ||
490 | 596 | _("Do you want to save the changes to this file in a different location?"); | ||
491 | 597 | |||
492 | 598 | set_message (Gtk.MessageType.WARNING, message, _("Save changes elsewhere"), () => { | ||
499 | 599 | this.save_as (); | 555 | this.save_as (); |
500 | 600 | hide_info_bar (); | 556 | hide_info_bar (); |
501 | 601 | }); | 557 | }); |
534 | 602 | main_actions.get_action ("SaveFile").sensitive = false; | 558 | } else { |
535 | 603 | this.source_view.editable = !settings.autosave; | 559 | string message = _("File \"%s\" was deleted. Do you want to save it anyway?").printf ("<b>%s</b>".printf (get_basename ())); |
536 | 604 | } | 560 | |
537 | 605 | else { | 561 | set_message (Gtk.MessageType.WARNING, message, _("Save"), () => { |
538 | 606 | main_actions.get_action ("SaveFile").sensitive = true; | 562 | this.save (); |
539 | 607 | this.source_view.editable = true; | 563 | hide_info_bar (); |
540 | 608 | } | 564 | }); |
541 | 609 | // Detect external changes | 565 | } |
542 | 610 | FileHandler.load_content_from_file.begin (file, (obj, res) => { | 566 | main_actions.get_action ("SaveFile").sensitive = false; |
543 | 611 | var text = FileHandler.load_content_from_file.end (res); | 567 | this.source_view.editable = false; |
544 | 612 | if (text == null) { | 568 | return; |
545 | 613 | show_error_dialog (); | 569 | } |
546 | 614 | return; | 570 | // If the file can't be written |
547 | 615 | } | 571 | if (!can_write ()) { |
548 | 616 | if (!text.validate()) | 572 | string message = _("You cannot save changes on file \"%s\". Do you want to save the changes to this file in a different location?").printf ("<b>%s</b>".printf (get_basename ())); |
549 | 617 | text = file_content_to_utf8 (file, text); | 573 | |
550 | 618 | // Reload automatically if auto save is ON | 574 | set_message (Gtk.MessageType.WARNING, message, _("Save changes elsewhere"), () => { |
551 | 619 | if (last_saved_content != null && text != last_saved_content) { | 575 | this.save_as (); |
552 | 620 | if (settings.autosave) | 576 | hide_info_bar (); |
521 | 621 | this.source_view.set_text (text, false); | ||
522 | 622 | else { | ||
523 | 623 | string message = _("File ") + " \"<b>%s</b>\" ".printf (get_basename ()) + | ||
524 | 624 | _("was modified by an external application. Do you want to load it again or continue your editing?"); | ||
525 | 625 | |||
526 | 626 | set_message (Gtk.MessageType.WARNING, message, _("Load"), () => { | ||
527 | 627 | this.source_view.set_text (text, false); | ||
528 | 628 | hide_info_bar (); | ||
529 | 629 | }, _("Continue"), () => { | ||
530 | 630 | hide_info_bar (); | ||
531 | 631 | }); | ||
532 | 632 | } | ||
533 | 633 | } | ||
553 | 634 | }); | 577 | }); |
554 | 578 | main_actions.get_action ("SaveFile").sensitive = false; | ||
555 | 579 | this.source_view.editable = !settings.autosave; | ||
556 | 635 | } | 580 | } |
557 | 636 | else { | 581 | else { |
558 | 637 | main_actions.get_action ("SaveFile").sensitive = true; | 582 | main_actions.get_action ("SaveFile").sensitive = true; |
559 | 638 | this.source_view.editable = true; | 583 | this.source_view.editable = true; |
560 | 639 | } | 584 | } |
561 | 585 | // Detect external changes | ||
562 | 586 | FileHandler.load_content_from_file.begin (file, (obj, res) => { | ||
563 | 587 | var text = FileHandler.load_content_from_file.end (res); | ||
564 | 588 | if (text == null) { | ||
565 | 589 | show_error_dialog (); | ||
566 | 590 | return; | ||
567 | 591 | } | ||
568 | 592 | if (!text.validate ()) | ||
569 | 593 | text = file_content_to_utf8 (file, text); | ||
570 | 594 | // Reload automatically if auto save is ON | ||
571 | 595 | if (last_saved_content != null && text != last_saved_content) { | ||
572 | 596 | if (settings.autosave) | ||
573 | 597 | this.source_view.set_text (text, false); | ||
574 | 598 | else { | ||
575 | 599 | string message = _("File \"%s\" was modified by an external application. Do you want to load it again or continue your editing?").printf ("<b>%s</b>".printf (get_basename ())); | ||
576 | 600 | set_message (Gtk.MessageType.WARNING, message, _("Load"), () => { | ||
577 | 601 | this.source_view.set_text (text, false); | ||
578 | 602 | hide_info_bar (); | ||
579 | 603 | }, _("Continue"), () => { | ||
580 | 604 | hide_info_bar (); | ||
581 | 605 | }); | ||
582 | 606 | } | ||
583 | 607 | } | ||
584 | 608 | }); | ||
585 | 640 | } | 609 | } |
586 | 641 | 610 | ||
587 | 642 | // Set Undo/Redo action sensitive property | 611 | // Set Undo/Redo action sensitive property |
588 | 643 | public void check_undoable_actions () { | 612 | public void check_undoable_actions () { |
589 | 644 | main_actions.get_action ("Undo").sensitive = this.source_view.buffer.can_undo; | 613 | main_actions.get_action ("Undo").sensitive = this.source_view.buffer.can_undo; |
590 | 645 | main_actions.get_action ("Redo").sensitive = this.source_view.buffer.can_redo; | 614 | main_actions.get_action ("Redo").sensitive = this.source_view.buffer.can_redo; |
592 | 646 | main_actions.get_action ("Revert").sensitive = (file != null && original_content != source_view.buffer.text); | 615 | main_actions.get_action ("Revert").sensitive = (original_content != source_view.buffer.text); |
593 | 647 | } | 616 | } |
594 | 648 | 617 | ||
595 | 649 | // Set saved status | 618 | // Set saved status |
596 | @@ -676,35 +645,55 @@ | |||
597 | 676 | } | 645 | } |
598 | 677 | } | 646 | } |
599 | 678 | 647 | ||
603 | 679 | private void delete_backup () { | 648 | private void delete_backup (string? backup_path = null) { |
604 | 680 | var backup = File.new_for_path (this.file.get_path () + "~"); | 649 | |
605 | 681 | if (!backup.query_exists ()) | 650 | string backup_file = ""; |
606 | 651 | |||
607 | 652 | if (backup_path == null) | ||
608 | 653 | backup_file = file.get_path () + "~"; | ||
609 | 654 | else | ||
610 | 655 | backup_file = backup_path; | ||
611 | 656 | |||
612 | 657 | debug ("Backup file deleting: %s", backup_file); | ||
613 | 658 | |||
614 | 659 | var backup = File.new_for_path (backup_file); | ||
615 | 660 | if (backup == null || !backup.query_exists ()) { | ||
616 | 661 | debug ("Backup file doesn't exists: %s", backup.get_path ()); | ||
617 | 682 | return; | 662 | return; |
618 | 663 | } | ||
619 | 683 | try { | 664 | try { |
620 | 684 | backup.delete (); | 665 | backup.delete (); |
621 | 666 | debug ("Backup file deleted: %s", backup_file); | ||
622 | 685 | } catch (Error e) { | 667 | } catch (Error e) { |
623 | 686 | warning ("Cannot delete backup for file \"%s\": %s", get_basename (), e.message); | 668 | warning ("Cannot delete backup for file \"%s\": %s", get_basename (), e.message); |
624 | 687 | } | 669 | } |
625 | 688 | } | 670 | } |
626 | 689 | 671 | ||
627 | 672 | private bool delete_temporary_file (bool force = false) { | ||
628 | 673 | if (!is_file_temporary || (get_text ().length > 0 && !force)) | ||
629 | 674 | return false; | ||
630 | 675 | try { | ||
631 | 676 | file.delete (); | ||
632 | 677 | return true; | ||
633 | 678 | } catch (Error e) { | ||
634 | 679 | warning ("Cannot delete temporary file \"%s\": %s", file.get_uri (), e.message); | ||
635 | 680 | } | ||
636 | 681 | return false; | ||
637 | 682 | } | ||
638 | 683 | |||
639 | 690 | // Return true if the file is writable | 684 | // Return true if the file is writable |
640 | 691 | public bool can_write () { | 685 | public bool can_write () { |
641 | 692 | FileInfo info; | 686 | FileInfo info; |
642 | 693 | 687 | ||
643 | 694 | bool writable = false; | 688 | bool writable = false; |
644 | 695 | 689 | ||
645 | 696 | if (this.file == null) | ||
646 | 697 | return writable = true; | ||
647 | 698 | |||
648 | 699 | try { | 690 | try { |
649 | 700 | info = this.file.query_info (FileAttribute.ACCESS_CAN_WRITE, FileQueryInfoFlags.NONE, null); | 691 | info = this.file.query_info (FileAttribute.ACCESS_CAN_WRITE, FileQueryInfoFlags.NONE, null); |
650 | 701 | writable = info.get_attribute_boolean (FileAttribute.ACCESS_CAN_WRITE); | 692 | writable = info.get_attribute_boolean (FileAttribute.ACCESS_CAN_WRITE); |
651 | 702 | return writable; | 693 | return writable; |
652 | 703 | } catch (Error e) { | 694 | } catch (Error e) { |
657 | 704 | if (this.file != null ) { | 695 | warning ("query_info failed, but filename appears to be correct, allowing as new file"); |
658 | 705 | warning ("query_info failed, but filename appears to be correct, allowing as new file"); | 696 | writable = true; |
655 | 706 | writable = true; | ||
656 | 707 | } | ||
659 | 708 | return writable; | 697 | return writable; |
660 | 709 | } | 698 | } |
661 | 710 | } | 699 | } |
662 | @@ -715,11 +704,6 @@ | |||
663 | 715 | } | 704 | } |
664 | 716 | 705 | ||
665 | 717 | private void file_changed () { | 706 | private void file_changed () { |
666 | 718 | if (file == null) { | ||
667 | 719 | mounted = true; | ||
668 | 720 | return; | ||
669 | 721 | } | ||
670 | 722 | |||
671 | 723 | if (mount != null) { | 707 | if (mount != null) { |
672 | 724 | mount.unmounted.disconnect (unmounted_cb); | 708 | mount.unmounted.disconnect (unmounted_cb); |
673 | 725 | mount = null; | 709 | mount = null; |
674 | @@ -740,4 +724,4 @@ | |||
675 | 740 | mounted = false; | 724 | mounted = false; |
676 | 741 | } | 725 | } |
677 | 742 | } | 726 | } |
679 | 743 | } | 727 | } |
680 | 744 | \ No newline at end of file | 728 | \ No newline at end of file |
681 | 745 | 729 | ||
682 | === modified file 'src/Widgets/DocumentView.vala' | |||
683 | --- src/Widgets/DocumentView.vala 2014-06-02 16:22:42 +0000 | |||
684 | +++ src/Widgets/DocumentView.vala 2014-10-18 19:50:44 +0000 | |||
685 | @@ -85,11 +85,21 @@ | |||
686 | 85 | 85 | ||
687 | 86 | show_all (); | 86 | show_all (); |
688 | 87 | } | 87 | } |
689 | 88 | |||
690 | 89 | private string unsaved_file_path_builder () { | ||
691 | 90 | DateTime timestamp = new DateTime.now_local (); | ||
692 | 91 | string new_text_file = _("Text file from ") + timestamp.format ("%Y-%m-%d %H:%M:%S"); | ||
693 | 92 | |||
694 | 93 | return ScratchApp.instance.data_home_folder_unsaved + new_text_file; | ||
695 | 94 | } | ||
696 | 88 | 95 | ||
697 | 89 | public void new_document () { | 96 | public void new_document () { |
699 | 90 | var doc = new Document (window.main_actions); | 97 | File file = File.new_for_path (unsaved_file_path_builder ()); |
700 | 98 | file.create (FileCreateFlags.PRIVATE); | ||
701 | 99 | |||
702 | 100 | var doc = new Document (window.main_actions, file); | ||
703 | 91 | doc.create_page (); | 101 | doc.create_page (); |
705 | 92 | 102 | ||
706 | 93 | this.notebook.insert_tab (doc, -1); | 103 | this.notebook.insert_tab (doc, -1); |
707 | 94 | this.notebook.current = doc; | 104 | this.notebook.current = doc; |
708 | 95 | 105 | ||
709 | @@ -220,4 +230,4 @@ | |||
710 | 220 | } | 230 | } |
711 | 221 | } | 231 | } |
712 | 222 | } | 232 | } |
714 | 223 | } | 233 | } |
715 | 224 | \ No newline at end of file | 234 | \ No newline at end of file |
716 | 225 | 235 | ||
717 | === modified file 'src/Widgets/ToolBar.vala' | |||
718 | --- src/Widgets/ToolBar.vala 2014-08-11 17:35:47 +0000 | |||
719 | +++ src/Widgets/ToolBar.vala 2014-10-18 19:50:44 +0000 | |||
720 | @@ -39,19 +39,18 @@ | |||
721 | 39 | public AppMenu app_menu; | 39 | public AppMenu app_menu; |
722 | 40 | 40 | ||
723 | 41 | public Toolbar (Gtk.ActionGroup main_actions) { | 41 | public Toolbar (Gtk.ActionGroup main_actions) { |
724 | 42 | |||
725 | 43 | // Toolbar properties | 42 | // Toolbar properties |
726 | 44 | // compliant with elementary HIG | 43 | // compliant with elementary HIG |
727 | 45 | get_style_context ().add_class ("primary-toolbar"); | 44 | get_style_context ().add_class ("primary-toolbar"); |
728 | 46 | 45 | ||
729 | 47 | // Create ToolButtons | 46 | // Create ToolButtons |
737 | 48 | open_button = main_actions.get_action ("Open").create_tool_item() as Gtk.ToolButton; | 47 | open_button = main_actions.get_action ("Open").create_tool_item () as Gtk.ToolButton; |
738 | 49 | templates_button = main_actions.get_action ("Templates").create_tool_item() as Gtk.ToolButton; | 48 | templates_button = main_actions.get_action ("Templates").create_tool_item () as Gtk.ToolButton; |
739 | 50 | save_button = main_actions.get_action ("SaveFile").create_tool_item() as Gtk.ToolButton; | 49 | save_button = main_actions.get_action ("SaveFile").create_tool_item () as Gtk.ToolButton; |
740 | 51 | save_as_button = main_actions.get_action ("SaveFileAs").create_tool_item() as Gtk.ToolButton; | 50 | save_as_button = main_actions.get_action ("SaveFileAs").create_tool_item () as Gtk.ToolButton; |
741 | 52 | revert_button = main_actions.get_action ("Revert").create_tool_item() as Gtk.ToolButton; | 51 | revert_button = main_actions.get_action ("Revert").create_tool_item () as Gtk.ToolButton; |
742 | 53 | find_button = main_actions.get_action ("Fetch").create_tool_item() as Gtk.ToolButton; | 52 | find_button = main_actions.get_action ("Fetch").create_tool_item () as Gtk.ToolButton; |
743 | 54 | 53 | ||
744 | 55 | // Create Share and AppMenu | 54 | // Create Share and AppMenu |
745 | 56 | share_menu = new Gtk.Menu (); | 55 | share_menu = new Gtk.Menu (); |
746 | 57 | share_app_menu = new Granite.Widgets.ToolButtonWithMenu (new Image.from_icon_name ("document-export", IconSize.MENU), _("Share"), share_menu); | 56 | share_app_menu = new Granite.Widgets.ToolButtonWithMenu (new Image.from_icon_name ("document-export", IconSize.MENU), _("Share"), share_menu); |
Code looks mostly OK, here are some notes: folder_ unsaved seems to be the only reason you're making the Scratch instance a singleton, but that variable should be part of settings (e.g I want to configure that in dconf - or scratch Preferences if Dan allows - to use /tmp instead of my home dir) friendly, and there are more like this. Translatable strings should not be concatenated. ").printf ("<b>%s</b>".printf (get_basename ())).
* Singleton is not needed here, accessing data_home_
* string message = _("File ") + " \"<b>%s</b>\" ".printf (get_basename ()) + _("was modified by an external application. Do you want to load it again or continue your editing?");
This is not translation-
It can also be written like: _("File %s was modified by an external application.
I see that there are some of these coming from before your commits, so it's not 100% your code, but they should be fixed too (not necessarily on this branch), but at least the code we touch should get better after each touch.
* Add a description and a commit message to the merge proposal
I haven't tested this yet, this is just a code review, will test later, with the fixes.