Add an autosave timer.

Original patch by Marc Britten <marc.britten@gmail.com>.
This commit is contained in:
whitequark 2015-03-29 07:46:57 +03:00
parent 22d43d5b83
commit 71b7ad7f99
7 changed files with 191 additions and 23 deletions

View File

@ -85,6 +85,7 @@ int64_t SolveSpace::GetMilliseconds(void) {
@interface DeferredHandler : NSObject
+ (void) runLater:(id)dummy;
+ (void) runCallback;
+ (void) doAutosave;
@end
@implementation DeferredHandler
@ -95,18 +96,29 @@ int64_t SolveSpace::GetMilliseconds(void) {
SolveSpace::SS.GW.TimerCallback();
SolveSpace::SS.TW.TimerCallback();
}
+ (void) doAutosave {
SolveSpace::SS.Autosave();
}
@end
void SolveSpace::SetTimerFor(int milliseconds) {
static void Schedule(SEL selector, double interval) {
NSMethodSignature *signature = [[DeferredHandler class]
methodSignatureForSelector:@selector(runCallback)];
methodSignatureForSelector:selector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setSelector:@selector(runCallback)];
[invocation setSelector:selector];
[invocation setTarget:[DeferredHandler class]];
[NSTimer scheduledTimerWithTimeInterval:(milliseconds / 1000.0)
[NSTimer scheduledTimerWithTimeInterval:interval
invocation:invocation repeats:NO];
}
void SolveSpace::SetTimerFor(int milliseconds) {
Schedule(@selector(runCallback), milliseconds / 1000.0);
}
void SolveSpace::SetAutosaveTimerFor(int minutes) {
Schedule(@selector(doAutosave), minutes * 60.0);
}
void SolveSpace::ScheduleLater() {
[[NSRunLoop currentRunLoop]
performSelector:@selector(runLater:)
@ -798,6 +810,23 @@ int SolveSpace::SaveFileYesNoCancel(void) {
abort(); /* unreachable */
}
int SolveSpace::LoadAutosaveYesNo(void) {
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:
@"An autosave file is availible for this project."];
[alert setInformativeText:
@"Do you want to load the autosave file instead?"];
[alert addButtonWithTitle:@"Load"];
[alert addButtonWithTitle:@"Don't Load"];
switch([alert runModal]) {
case NSAlertFirstButtonReturn:
return SAVE_YES;
case NSAlertSecondButtonReturn:
return SAVE_NO;
}
abort(); /* unreachable */
}
/* Text window */
@interface TextWindowView : GLViewWithEditor

View File

@ -167,6 +167,14 @@ void TextWindow::ScreenChangeGCodeParameter(int link, uint32_t v) {
SS.TW.ShowEditControl(row, 14, buf);
}
void TextWindow::ScreenChangeAutosaveInterval(int link, uint32_t v) {
char str[1024];
sprintf(str, "%d", SS.autosaveInterval);
SS.TW.ShowEditControl(111, 3, str);
SS.TW.edit.meaning = EDIT_AUTOSAVE_INTERVAL;
}
void TextWindow::ShowConfiguration(void) {
int i;
Printf(true, "%Ft user color (r, g, b)");
@ -294,6 +302,11 @@ void TextWindow::ShowConfiguration(void) {
&ScreenChangeCheckClosedContour,
SS.checkClosedContour ? CHECK_TRUE : CHECK_FALSE);
Printf(false, "");
Printf(false, "%Ft autosave interval (in minutes)%E");
Printf(false, "%Ba %d %Fl%Ll%f[change]%E",
SS.autosaveInterval, &ScreenChangeAutosaveInterval);
Printf(false, "");
Printf(false, " %Ftgl vendor %E%s", glGetString(GL_VENDOR));
Printf(false, " %Ft renderer %E%s", glGetString(GL_RENDERER));
@ -425,6 +438,19 @@ bool TextWindow::EditControlDoneForConfiguration(const char *s) {
if(e) SS.gCode.plungeFeed = (float)SS.ExprToMm(e);
break;
}
case EDIT_AUTOSAVE_INTERVAL: {
int interval;
if(sscanf(s, "%d", &interval)==1) {
if(interval >= 1) {
SS.autosaveInterval = interval;
SetAutosaveTimerFor(interval);
} else {
Error("Bad value: autosave interval should be positive");
}
} else {
Error("Bad format: specify interval in integral minutes");
}
}
default: return false;
}

View File

@ -224,7 +224,7 @@ static void CnfThawWindowPos(Gtk::Window *win, const char *key) {
win->resize(w, h);
}
/* Timer */
/* Timers */
int64_t GetMilliseconds(void) {
struct timespec ts;
@ -242,6 +242,15 @@ void SetTimerFor(int milliseconds) {
Glib::signal_timeout().connect(&TimerCallback, milliseconds);
}
static bool AutosaveTimerCallback() {
SS.Autosave();
return false;
}
void SetAutosaveTimerFor(int minutes) {
Glib::signal_timeout().connect(&AutosaveTimerCallback, minutes * 60 * 1000);
}
static bool LaterCallback() {
SS.DoLater();
return false;
@ -1186,7 +1195,7 @@ int SaveFileYesNoCancel(void) {
Gtk::BUTTONS_NONE, /*is_modal*/ true);
dialog.set_title("SolveSpace - Modified File");
dialog.add_button("_Save", Gtk::RESPONSE_YES);
dialog.add_button("Do_n't save", Gtk::RESPONSE_NO);
dialog.add_button("Do_n't Save", Gtk::RESPONSE_NO);
dialog.add_button("_Cancel", Gtk::RESPONSE_CANCEL);
switch(dialog.run()) {
@ -1202,6 +1211,26 @@ int SaveFileYesNoCancel(void) {
}
}
int LoadAutosaveYesNo(void) {
Glib::ustring message =
"An autosave file is availible for this project.\n"
"Do you want to load the autosave file instead?";
Gtk::MessageDialog dialog(*GW, message, /*use_markup*/ true, Gtk::MESSAGE_QUESTION,
Gtk::BUTTONS_NONE, /*is_modal*/ true);
dialog.set_title("SolveSpace - Autosave Available");
dialog.add_button("_Load autosave", Gtk::RESPONSE_YES);
dialog.add_button("Do_n't Load", Gtk::RESPONSE_NO);
switch(dialog.run()) {
case Gtk::RESPONSE_YES:
return SAVE_YES;
case Gtk::RESPONSE_NO:
default:
return SAVE_NO;
}
}
/* Text window */
class TextWidget : public GlWidget {

View File

@ -91,24 +91,49 @@ void SolveSpaceUI::Init() {
CnfThawString(RecentFile[i], MAX_PATH, name);
}
RefreshRecentMenus();
// Autosave timer
autosaveInterval = CnfThawInt(5, "AutosaveInterval");
// The default styles (colors, line widths, etc.) are also stored in the
// configuration file, but we will automatically load those as we need
// them.
SetAutosaveTimerFor(autosaveInterval);
NewFile();
AfterNewFile();
}
bool SolveSpaceUI::LoadAutosaveFor(const char *filename) {
char autosaveFile[MAX_PATH];
strcpy(autosaveFile, filename);
strcat(autosaveFile, AUTOSAVE_SUFFIX);
FILE *f = fopen(autosaveFile, "r");
if(!f)
return false;
fclose(f);
if(LoadAutosaveYesNo() == SAVE_YES) {
unsaved = true;
return LoadFromFile(autosaveFile);
}
return false;
}
bool SolveSpaceUI::OpenFile(const char *filename) {
bool success = LoadFromFile(filename);
bool autosaveLoaded = LoadAutosaveFor(filename);
bool success = autosaveLoaded || LoadFromFile(filename);
if(success) {
RemoveAutosave();
AddToRecentList(filename);
strcpy(saveFile, filename);
} else {
NewFile();
}
AfterNewFile();
unsaved = autosaveLoaded;
return success;
}
@ -183,10 +208,15 @@ void SolveSpaceUI::Exit(void) {
CnfFreezeFloat(gCode.plungeFeed, "GCode_PlungeFeed");
// Show toolbar in the graphics window
CnfFreezeBool(showToolbar, "ShowToolbar");
// Autosave timer
CnfFreezeInt(autosaveInterval, "AutosaveInterval");
// And the default styles, colors and line widths and such.
Style::FreezeDefaultStyles();
// Exiting cleanly.
RemoveAutosave();
ExitNow();
}
@ -345,6 +375,7 @@ bool SolveSpaceUI::GetFilenameAndSave(bool saveAs) {
if(SaveToFile(saveFile)) {
AddToRecentList(saveFile);
RemoveAutosave();
unsaved = false;
return true;
} else {
@ -354,6 +385,28 @@ bool SolveSpaceUI::GetFilenameAndSave(bool saveAs) {
}
}
bool SolveSpaceUI::Autosave()
{
SetAutosaveTimerFor(autosaveInterval);
if (strlen(saveFile) != 0 && unsaved) {
char autosaveFile[MAX_PATH];
strcpy(autosaveFile, saveFile);
strcat(autosaveFile, AUTOSAVE_SUFFIX);
return SaveToFile(autosaveFile);
}
return false;
}
void SolveSpaceUI::RemoveAutosave()
{
char autosaveFile[MAX_PATH];
strcpy(autosaveFile, saveFile);
strcat(autosaveFile, AUTOSAVE_SUFFIX);
remove(autosaveFile);
}
bool SolveSpaceUI::OkayToStartNewFile(void) {
if(!unsaved) return true;
@ -393,14 +446,7 @@ void SolveSpaceUI::MenuFile(int id) {
char newFile[MAX_PATH];
strcpy(newFile, RecentFile[id-RECENT_OPEN]);
RemoveFromRecentList(newFile);
if(SS.LoadFromFile(newFile)) {
strcpy(SS.saveFile, newFile);
AddToRecentList(newFile);
} else {
strcpy(SS.saveFile, "");
SS.NewFile();
}
SS.AfterNewFile();
SS.OpenFile(newFile);
return;
}
@ -418,14 +464,7 @@ void SolveSpaceUI::MenuFile(int id) {
char newFile[MAX_PATH] = "";
if(GetOpenFile(newFile, SLVS_EXT, SLVS_PATTERN)) {
if(SS.LoadFromFile(newFile)) {
strcpy(SS.saveFile, newFile);
AddToRecentList(newFile);
} else {
strcpy(SS.saveFile, "");
SS.NewFile();
}
SS.AfterNewFile();
SS.OpenFile(newFile);
}
break;
}

View File

@ -133,6 +133,9 @@ void RefreshRecentMenus(void);
#define SAVE_NO (-1)
#define SAVE_CANCEL (0)
int SaveFileYesNoCancel(void);
int LoadAutosaveYesNo(void);
#define AUTOSAVE_SUFFIX "~"
#if defined(HAVE_GTK)
// Selection pattern format to be parsed by GTK3 glue code:
@ -242,6 +245,7 @@ void SetCurrentFilename(const char *filename);
void SetMousePointerToHand(bool yes);
void DoMessageBox(const char *str, int rows, int cols, bool error);
void SetTimerFor(int milliseconds);
void SetAutosaveTimerFor(int minutes);
void ScheduleLater();
void ExitNow(void);
@ -768,6 +772,7 @@ public:
Unit viewUnits;
int afterDecimalMm;
int afterDecimalInch;
int autosaveInterval; // in minutes
char *MmToString(double v);
double ExprToMm(Expr *e);
@ -818,6 +823,8 @@ public:
Style s;
} sv;
static void MenuFile(int id);
bool Autosave();
void RemoveAutosave();
bool GetFilenameAndSave(bool saveAs);
bool OkayToStartNewFile(void);
hGroup CreateDefaultDrawingGroup(void);
@ -825,6 +832,7 @@ public:
void ClearExisting(void);
void NewFile(void);
bool SaveToFile(const char *filename);
bool LoadAutosaveFor(const char *filename);
bool LoadFromFile(const char *filename);
bool LoadEntitiesFromFile(const char *filename, EntityList *le,
SMesh *m, SShell *sh);

View File

@ -155,6 +155,7 @@ public:
EDIT_G_CODE_PASSES = 121,
EDIT_G_CODE_FEED = 122,
EDIT_G_CODE_PLUNGE_FEED = 123,
EDIT_AUTOSAVE_INTERVAL = 124,
// For TTF text
EDIT_TTF_TEXT = 300,
// For the step dimension screen
@ -297,6 +298,7 @@ public:
static void ScreenChangeExportScale(int link, uint32_t v);
static void ScreenChangeExportOffset(int link, uint32_t v);
static void ScreenChangeGCodeParameter(int link, uint32_t v);
static void ScreenChangeAutosaveInterval(int link, uint32_t v);
static void ScreenChangeStyleName(int link, uint32_t v);
static void ScreenChangeStyleWidthOrTextHeight(int link, uint32_t v);
static void ScreenChangeStyleTextAngle(int link, uint32_t v);

View File

@ -248,6 +248,17 @@ void SolveSpace::ScheduleLater()
{
}
static void CALLBACK AutosaveCallback(HWND hwnd, UINT msg, UINT_PTR id, DWORD time)
{
KillTimer(GraphicsWnd, 1);
SS.Autosave();
}
void SolveSpace::SetAutosaveTimerFor(int minutes)
{
SetTimer(GraphicsWnd, 2, minutes * 60 * 1000, AutosaveCallback);
}
static void GetWindowSize(HWND hwnd, int *w, int *h)
{
RECT r;
@ -880,6 +891,7 @@ bool SolveSpace::GetOpenFile(char *file, const char *defExtension, const char *s
return r ? true : false;
}
bool SolveSpace::GetSaveFile(char *file, const char *defExtension, const char *selPattern)
{
OPENFILENAME ofn;
@ -905,6 +917,7 @@ bool SolveSpace::GetSaveFile(char *file, const char *defExtension, const char *s
return r ? true : false;
}
int SolveSpace::SaveFileYesNoCancel(void)
{
EnableWindow(GraphicsWnd, false);
@ -929,6 +942,28 @@ int SolveSpace::SaveFileYesNoCancel(void)
return SAVE_CANCEL;
}
int SolveSpace::LoadAutosaveYesNo(void)
{
EnableWindow(GraphicsWnd, false);
EnableWindow(TextWnd, false);
int r = MessageBox(GraphicsWnd,
"An autosave file is availible for this project.\r\n\r\n"
"Do you want to load the autosave file instead?", "SolveSpace",
MB_YESNO | MB_ICONWARNING);
EnableWindow(TextWnd, true);
EnableWindow(GraphicsWnd, true);
SetForegroundWindow(GraphicsWnd);
switch (r) {
case IDYES: return SAVE_YES;
case IDNO: return SAVE_NO;
}
oops();
}
void SolveSpace::LoadAllFontFiles(void)
{
WIN32_FIND_DATA wfd;