summaryrefslogtreecommitdiff
path: root/src/ui/inputwidget.c
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-07-23 10:09:38 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-07-23 10:09:38 +0300
commit7f54c3fb6caa0bc109ed47be4bc91039a00bed80 (patch)
tree04375ce5b1c75aafac682b2f200edd42e52fb5d4 /src/ui/inputwidget.c
parentbb09db3b0176b01d10b5c5ed339ee67e2e753f42 (diff)
InputWidget: Automatic backup mode
Diffstat (limited to 'src/ui/inputwidget.c')
-rw-r--r--src/ui/inputwidget.c123
1 files changed, 117 insertions, 6 deletions
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c
index c5a82680..cb6d35a4 100644
--- a/src/ui/inputwidget.c
+++ b/src/ui/inputwidget.c
@@ -30,6 +30,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
30#include "app.h" 30#include "app.h"
31 31
32#include <the_Foundation/array.h> 32#include <the_Foundation/array.h>
33#include <the_Foundation/file.h>
34#include <the_Foundation/path.h>
33#include <SDL_clipboard.h> 35#include <SDL_clipboard.h>
34#include <SDL_timer.h> 36#include <SDL_timer.h>
35 37
@@ -87,13 +89,21 @@ static void clearInputLines_(iArray *inputLines) {
87static void splitToLines_(const iString *text, iArray *inputLines) { 89static void splitToLines_(const iString *text, iArray *inputLines) {
88 clearInputLines_(inputLines); 90 clearInputLines_(inputLines);
89 if (isEmpty_String(text)) { 91 if (isEmpty_String(text)) {
90 iInputLine line; 92 iInputLine empty;
91 init_InputLine(&line); 93 init_InputLine(&empty);
92 pushBack_Array(inputLines, &line); 94 pushBack_Array(inputLines, &empty);
93 return; 95 return;
94 } 96 }
95 size_t index = 0; 97 size_t index = 0;
96 iRangecc seg = iNullRange; 98 iRangecc seg = iNullRange;
99 if (startsWith_String(text, "\n")) { /* empty segment ignored at the start */
100 iInputLine empty;
101 init_InputLine(&empty);
102 setCStr_String(&empty.text, "\n");
103 empty.range = (iRanges){ 0, 1 };
104 index = 1;
105 pushBack_Array(inputLines, &empty);
106 }
97 while (nextSplit_Rangecc(range_String(text), "\n", &seg)) { 107 while (nextSplit_Rangecc(range_String(text), "\n", &seg)) {
98 iInputLine line; 108 iInputLine line;
99 init_InputLine(&line); 109 init_InputLine(&line);
@@ -103,7 +113,14 @@ static void splitToLines_(const iString *text, iArray *inputLines) {
103 pushBack_Array(inputLines, &line); 113 pushBack_Array(inputLines, &line);
104 index = line.range.end; 114 index = line.range.end;
105 } 115 }
106 if (!endsWith_String(text, "\n")) { 116 if (endsWith_String(text, "\n")) { /* empty segment ignored at the end */
117 iInputLine empty;
118 init_InputLine(&empty);
119 iInputLine *last = back_Array(inputLines);
120 empty.range.start = empty.range.end = last->range.end;
121 pushBack_Array(inputLines, &empty);
122 }
123 else {
107 iInputLine *last = back_Array(inputLines); 124 iInputLine *last = back_Array(inputLines);
108 removeEnd_String(&last->text, 1); 125 removeEnd_String(&last->text, 1);
109 last->range.end--; 126 last->range.end--;
@@ -168,7 +185,9 @@ enum iInputWidgetFlag {
168 markWords_InputWidgetFlag = iBit(8), 185 markWords_InputWidgetFlag = iBit(8),
169 needUpdateBuffer_InputWidgetFlag = iBit(9), 186 needUpdateBuffer_InputWidgetFlag = iBit(9),
170 enterKeyEnabled_InputWidgetFlag = iBit(10), 187 enterKeyEnabled_InputWidgetFlag = iBit(10),
171 enterKeyInsertsLineFeed_InputWidgetFlag = iBit(11), 188 enterKeyInsertsLineFeed_InputWidgetFlag
189 = iBit(11),
190 needBackup_InputWidgetFlag = iBit(12),
172}; 191};
173 192
174/*----------------------------------------------------------------------------------------------*/ 193/*----------------------------------------------------------------------------------------------*/
@@ -199,10 +218,84 @@ struct Impl_InputWidget {
199 iTextBuf * buffered; /* pre-rendered static text */ 218 iTextBuf * buffered; /* pre-rendered static text */
200 iInputWidgetValidatorFunc validator; 219 iInputWidgetValidatorFunc validator;
201 void * validatorContext; 220 void * validatorContext;
221 iString * backupPath;
222 int backupTimer;
202}; 223};
203 224
204iDefineObjectConstructionArgs(InputWidget, (size_t maxLen), maxLen) 225iDefineObjectConstructionArgs(InputWidget, (size_t maxLen), maxLen)
205 226
227static void restoreBackup_InputWidget_(iInputWidget *d) {
228 if (!d->backupPath) return;
229 iFile *f = new_File(d->backupPath);
230 if (open_File(f, readOnly_FileMode | text_FileMode)) {
231 setText_InputWidget(d, collect_String(readString_File(f)));
232 }
233 iRelease(f);
234}
235
236static void saveBackup_InputWidget_(iInputWidget *d) {
237 if (!d->backupPath) return;
238 iFile *f = new_File(d->backupPath);
239 if (open_File(f, writeOnly_FileMode | text_FileMode)) {
240 iConstForEach(Array, i, &d->lines) {
241 const iInputLine *line = i.value;
242 write_File(f, utf8_String(&line->text));
243 }
244 d->inFlags &= ~needBackup_InputWidgetFlag;
245#if !defined (NDEBUG)
246 iConstForEach(Array, j, &d->lines) {
247 iAssert(endsWith_String(&((const iInputLine *) j.value)->text, "\n") ||
248 index_ArrayConstIterator(&j) == size_Array(&d->lines) - 1);
249 }
250#endif
251 }
252 iRelease(f);
253}
254
255static void eraseBackup_InputWidget_(iInputWidget *d) {
256 if (d->backupPath) {
257 remove(cstr_String(d->backupPath));
258 delete_String(d->backupPath);
259 d->backupPath = NULL;
260 }
261}
262
263static uint32_t backupTimeout_InputWidget_(uint32_t interval, void *context) {
264 iInputWidget *d = context;
265 postCommand_Widget(d, "input.backup");
266 return 0;
267}
268
269static void restartBackupTimer_InputWidget_(iInputWidget *d) {
270 if (d->backupPath) {
271 d->inFlags |= needBackup_InputWidgetFlag;
272 if (d->backupTimer) {
273 SDL_RemoveTimer(d->backupTimer);
274 }
275 d->backupTimer = SDL_AddTimer(2500, backupTimeout_InputWidget_, d);
276 }
277}
278
279void setBackupFileName_InputWidget(iInputWidget *d, const char *fileName) {
280 if (fileName == NULL) {
281 if (d->backupTimer) {
282 SDL_RemoveTimer(d->backupTimer);
283 d->backupTimer = 0;
284 }
285 eraseBackup_InputWidget_(d);
286 if (d->backupPath) {
287 delete_String(d->backupPath);
288 d->backupPath = NULL;
289 }
290 return;
291 }
292 if (!d->backupPath) {
293 d->backupPath = copy_String(dataDir_App());
294 }
295 append_Path(d->backupPath, collectNewCStr_String(fileName));
296 restoreBackup_InputWidget_(d);
297}
298
206static void clearUndo_InputWidget_(iInputWidget *d) { 299static void clearUndo_InputWidget_(iInputWidget *d) {
207 iForEach(Array, i, &d->undoStack) { 300 iForEach(Array, i, &d->undoStack) {
208 deinit_InputUndo_(i.value); 301 deinit_InputUndo_(i.value);
@@ -635,11 +728,21 @@ void init_InputWidget(iInputWidget *d, size_t maxLen) {
635 d->timer = 0; 728 d->timer = 0;
636 d->cursorVis = 0; 729 d->cursorVis = 0;
637 d->buffered = NULL; 730 d->buffered = NULL;
731 d->backupPath = NULL;
732 d->backupTimer = 0;
638 //updateLines_InputWidget_(d); 733 //updateLines_InputWidget_(d);
639 updateMetrics_InputWidget_(d); 734 updateMetrics_InputWidget_(d);
640} 735}
641 736
642void deinit_InputWidget(iInputWidget *d) { 737void deinit_InputWidget(iInputWidget *d) {
738 if (d->backupTimer) {
739 SDL_RemoveTimer(d->backupTimer);
740 }
741 if (d->inFlags & needBackup_InputWidgetFlag) {
742 saveBackup_InputWidget_(d);
743 }
744 delete_String(d->backupPath);
745 d->backupPath = NULL;
643 clearInputLines_(&d->lines); 746 clearInputLines_(&d->lines);
644 if (isSelected_Widget(d)) { 747 if (isSelected_Widget(d)) {
645 SDL_StopTextInput(); 748 SDL_StopTextInput();
@@ -1016,6 +1119,7 @@ static void textOfLinesWasChanged_InputWidget_(iInputWidget *d, iRangei lineRang
1016 updateLineRangesStartingFrom_InputWidget_(d, lineRange.start); 1119 updateLineRangesStartingFrom_InputWidget_(d, lineRange.start);
1017 updateVisible_InputWidget_(d); 1120 updateVisible_InputWidget_(d);
1018 updateMetrics_InputWidget_(d); 1121 updateMetrics_InputWidget_(d);
1122 restartBackupTimer_InputWidget_(d);
1019} 1123}
1020 1124
1021static void insertRange_InputWidget_(iInputWidget *d, iRangecc range) { 1125static void insertRange_InputWidget_(iInputWidget *d, iRangecc range) {
@@ -1239,6 +1343,7 @@ static void contentsWereChanged_InputWidget_(iInputWidget *d) {
1239 1343
1240static void deleteIndexRange_InputWidget_(iInputWidget *d, iRanges deleted) { 1344static void deleteIndexRange_InputWidget_(iInputWidget *d, iRanges deleted) {
1241 size_t firstModified = iInvalidPos; 1345 size_t firstModified = iInvalidPos;
1346 restartBackupTimer_InputWidget_(d);
1242 for (int i = size_Array(&d->lines) - 1; i >= 0; i--) { 1347 for (int i = size_Array(&d->lines) - 1; i >= 0; i--) {
1243 iInputLine *line = at_Array(&d->lines, i); 1348 iInputLine *line = at_Array(&d->lines, i);
1244 if (line->range.end <= deleted.start) { 1349 if (line->range.end <= deleted.start) {
@@ -1545,6 +1650,12 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) {
1545 contentsWereChanged_InputWidget_(d); 1650 contentsWereChanged_InputWidget_(d);
1546 return iTrue; 1651 return iTrue;
1547 } 1652 }
1653 else if (isCommand_Widget(w, ev, "input.backup")) {
1654 if (d->inFlags & needBackup_InputWidgetFlag) {
1655 saveBackup_InputWidget_(d);
1656 }
1657 return iTrue;
1658 }
1548 else if (isMetricsChange_UserEvent(ev)) { 1659 else if (isMetricsChange_UserEvent(ev)) {
1549 updateMetrics_InputWidget_(d); 1660 updateMetrics_InputWidget_(d);
1550 // updateLinesAndResize_InputWidget_(d); 1661 // updateLinesAndResize_InputWidget_(d);
@@ -1708,7 +1819,7 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) {
1708 } 1819 }
1709 return iFalse; 1820 return iFalse;
1710 case SDLK_ESCAPE: 1821 case SDLK_ESCAPE:
1711 end_InputWidget(d, iFalse); 1822 end_InputWidget(d, iTrue);
1712 setFocus_Widget(NULL); 1823 setFocus_Widget(NULL);
1713 return (d->inFlags & eatEscape_InputWidgetFlag) != 0; 1824 return (d->inFlags & eatEscape_InputWidgetFlag) != 0;
1714 case SDLK_BACKSPACE: 1825 case SDLK_BACKSPACE: