diff options
Diffstat (limited to 'src/app.c')
-rw-r--r-- | src/app.c | 553 |
1 files changed, 401 insertions, 152 deletions
@@ -125,7 +125,8 @@ struct Impl_App { | |||
125 | iGmCerts * certs; | 125 | iGmCerts * certs; |
126 | iVisited * visited; | 126 | iVisited * visited; |
127 | iBookmarks * bookmarks; | 127 | iBookmarks * bookmarks; |
128 | iMainWindow *window; | 128 | iMainWindow *window; /* currently active MainWindow */ |
129 | iPtrArray mainWindows; | ||
129 | iPtrArray popupWindows; | 130 | iPtrArray popupWindows; |
130 | iSortedArray tickers; /* per-frame callbacks, used for animations */ | 131 | iSortedArray tickers; /* per-frame callbacks, used for animations */ |
131 | uint32_t lastTickerTime; | 132 | uint32_t lastTickerTime; |
@@ -153,7 +154,8 @@ struct Impl_App { | |||
153 | /* Preferences: */ | 154 | /* Preferences: */ |
154 | iBool commandEcho; /* --echo */ | 155 | iBool commandEcho; /* --echo */ |
155 | iBool forceSoftwareRender; /* --sw */ | 156 | iBool forceSoftwareRender; /* --sw */ |
156 | iRect initialWindowRect; | 157 | //iRect initialWindowRect; |
158 | iArray initialWindowRects; /* one per window */ | ||
157 | iPrefs prefs; | 159 | iPrefs prefs; |
158 | }; | 160 | }; |
159 | 161 | ||
@@ -199,33 +201,43 @@ static iString *serializePrefs_App_(const iApp *d) { | |||
199 | appendFormat_String(str, "window.retain arg:%d\n", d->prefs.retainWindowSize); | 201 | appendFormat_String(str, "window.retain arg:%d\n", d->prefs.retainWindowSize); |
200 | if (d->prefs.retainWindowSize) { | 202 | if (d->prefs.retainWindowSize) { |
201 | int w, h, x, y; | 203 | int w, h, x, y; |
202 | x = d->window->place.normalRect.pos.x; | 204 | iConstForEach(PtrArray, i, &d->mainWindows) { |
203 | y = d->window->place.normalRect.pos.y; | 205 | const iMainWindow *win = i.ptr; |
204 | w = d->window->place.normalRect.size.x; | 206 | const size_t winIndex = index_PtrArrayConstIterator(&i); |
205 | h = d->window->place.normalRect.size.y; | 207 | x = win->place.normalRect.pos.x; |
206 | appendFormat_String(str, "window.setrect width:%d height:%d coord:%d %d\n", w, h, x, y); | 208 | y = win->place.normalRect.pos.y; |
207 | /* On macOS, maximization should be applied at creation time or the window will take | 209 | w = win->place.normalRect.size.x; |
208 | a moment to animate to its maximized size. */ | 210 | h = win->place.normalRect.size.y; |
211 | appendFormat_String(str, | ||
212 | "window.setrect index:%zu width:%d height:%d coord:%d %d\n", | ||
213 | winIndex, | ||
214 | w, | ||
215 | h, | ||
216 | x, | ||
217 | y); | ||
218 | /* On macOS, maximization should be applied at creation time or the window will take | ||
219 | a moment to animate to its maximized size. */ | ||
209 | #if defined (LAGRANGE_ENABLE_CUSTOM_FRAME) | 220 | #if defined (LAGRANGE_ENABLE_CUSTOM_FRAME) |
210 | if (snap_MainWindow(d->window)) { | 221 | if (snap_MainWindow(win)) { |
211 | if (snap_MainWindow(d->window) == maximized_WindowSnap) { | 222 | if (snap_MainWindow(win) == maximized_WindowSnap) { |
212 | appendFormat_String(str, "~window.maximize\n"); | 223 | appendFormat_String(str, "~window.maximize index:%zu\n", winIndex); |
213 | } | 224 | } |
214 | else if (~SDL_GetWindowFlags(d->window->base.win) & SDL_WINDOW_MINIMIZED) { | 225 | else if (~SDL_GetWindowFlags(win->base.win) & SDL_WINDOW_MINIMIZED) { |
215 | /* Save the actual visible window position, too, because snapped windows may | 226 | /* Save the actual visible window position, too, because snapped windows may |
216 | still be resized/moved without affecting normalRect. */ | 227 | still be resized/moved without affecting normalRect. */ |
217 | SDL_GetWindowPosition(d->window->base.win, &x, &y); | 228 | SDL_GetWindowPosition(win->base.win, &x, &y); |
218 | SDL_GetWindowSize(d->window->base.win, &w, &h); | 229 | SDL_GetWindowSize(win->base.win, &w, &h); |
219 | appendFormat_String( | 230 | appendFormat_String( |
220 | str, "~window.setrect snap:%d width:%d height:%d coord:%d %d\n", | 231 | str, "~window.setrect index:%zu snap:%d width:%d height:%d coord:%d %d\n", |
221 | snap_MainWindow(d->window), w, h, x, y); | 232 | winIndex, snap_MainWindow(d->window), w, h, x, y); |
233 | } | ||
222 | } | 234 | } |
223 | } | ||
224 | #elif !defined (iPlatformApple) | 235 | #elif !defined (iPlatformApple) |
225 | if (snap_MainWindow(d->window) == maximized_WindowSnap) { | 236 | if (snap_MainWindow(win) == maximized_WindowSnap) { |
226 | appendFormat_String(str, "~window.maximize\n"); | 237 | appendFormat_String(str, "~window.maximize index:%zu\n", winIndex); |
227 | } | 238 | } |
228 | #endif | 239 | #endif |
240 | } | ||
229 | } | 241 | } |
230 | appendFormat_String(str, "uilang id:%s\n", cstr_String(&d->prefs.strings[uiLanguage_PrefsString])); | 242 | appendFormat_String(str, "uilang id:%s\n", cstr_String(&d->prefs.strings[uiLanguage_PrefsString])); |
231 | appendFormat_String(str, "uiscale arg:%f\n", uiScale_Window(as_Window(d->window))); | 243 | appendFormat_String(str, "uiscale arg:%f\n", uiScale_Window(as_Window(d->window))); |
@@ -244,6 +256,7 @@ static iString *serializePrefs_App_(const iApp *d) { | |||
244 | appendFormat_String(str, "imageloadscroll arg:%d\n", d->prefs.loadImageInsteadOfScrolling); | 256 | appendFormat_String(str, "imageloadscroll arg:%d\n", d->prefs.loadImageInsteadOfScrolling); |
245 | appendFormat_String(str, "cachesize.set arg:%d\n", d->prefs.maxCacheSize); | 257 | appendFormat_String(str, "cachesize.set arg:%d\n", d->prefs.maxCacheSize); |
246 | appendFormat_String(str, "memorysize.set arg:%d\n", d->prefs.maxMemorySize); | 258 | appendFormat_String(str, "memorysize.set arg:%d\n", d->prefs.maxMemorySize); |
259 | appendFormat_String(str, "urlsize.set arg:%d\n", d->prefs.maxUrlSize); | ||
247 | appendFormat_String(str, "decodeurls arg:%d\n", d->prefs.decodeUserVisibleURLs); | 260 | appendFormat_String(str, "decodeurls arg:%d\n", d->prefs.decodeUserVisibleURLs); |
248 | appendFormat_String(str, "linewidth.set arg:%d\n", d->prefs.lineWidth); | 261 | appendFormat_String(str, "linewidth.set arg:%d\n", d->prefs.lineWidth); |
249 | appendFormat_String(str, "linespacing.set arg:%f\n", d->prefs.lineSpacing); | 262 | appendFormat_String(str, "linespacing.set arg:%f\n", d->prefs.lineSpacing); |
@@ -279,6 +292,7 @@ static iString *serializePrefs_App_(const iApp *d) { | |||
279 | { "prefs.collapsepreonload", &d->prefs.collapsePreOnLoad }, | 292 | { "prefs.collapsepreonload", &d->prefs.collapsePreOnLoad }, |
280 | { "prefs.hoverlink", &d->prefs.hoverLink }, | 293 | { "prefs.hoverlink", &d->prefs.hoverLink }, |
281 | { "prefs.bookmarks.addbottom", &d->prefs.addBookmarksToBottom }, | 294 | { "prefs.bookmarks.addbottom", &d->prefs.addBookmarksToBottom }, |
295 | { "prefs.dataurl.openimages", &d->prefs.openDataUrlImagesOnLoad }, | ||
282 | { "prefs.archive.openindex", &d->prefs.openArchiveIndexPages }, | 296 | { "prefs.archive.openindex", &d->prefs.openArchiveIndexPages }, |
283 | { "prefs.font.warnmissing", &d->prefs.warnAboutMissingGlyphs }, | 297 | { "prefs.font.warnmissing", &d->prefs.warnAboutMissingGlyphs }, |
284 | { "prefs.blink", &d->prefs.blinkingCursor }, | 298 | { "prefs.blink", &d->prefs.blinkingCursor }, |
@@ -401,9 +415,14 @@ static void loadPrefs_App_(iApp *d) { | |||
401 | d->prefs.customFrame = arg_Command(cmd); | 415 | d->prefs.customFrame = arg_Command(cmd); |
402 | } | 416 | } |
403 | else if (equal_Command(cmd, "window.setrect") && !argLabel_Command(cmd, "snap")) { | 417 | else if (equal_Command(cmd, "window.setrect") && !argLabel_Command(cmd, "snap")) { |
404 | const iInt2 pos = coord_Command(cmd); | 418 | const int index = argLabel_Command(cmd, "index"); |
405 | d->initialWindowRect = init_Rect( | 419 | const iInt2 pos = coord_Command(cmd); |
420 | iRect winRect = init_Rect( | ||
406 | pos.x, pos.y, argLabel_Command(cmd, "width"), argLabel_Command(cmd, "height")); | 421 | pos.x, pos.y, argLabel_Command(cmd, "width"), argLabel_Command(cmd, "height")); |
422 | if (index >= 0 && index < 100) { | ||
423 | resize_Array(&d->initialWindowRects, index + 1); | ||
424 | set_Array(&d->initialWindowRects, index, &winRect); | ||
425 | } | ||
407 | } | 426 | } |
408 | else if (equal_Command(cmd, "fontpack.disable")) { | 427 | else if (equal_Command(cmd, "fontpack.disable")) { |
409 | insert_StringSet(d->prefs.disabledFontPacks, | 428 | insert_StringSet(d->prefs.disabledFontPacks, |
@@ -439,6 +458,7 @@ static void loadPrefs_App_(iApp *d) { | |||
439 | } | 458 | } |
440 | iRelease(f); | 459 | iRelease(f); |
441 | /* Upgrade checks. */ | 460 | /* Upgrade checks. */ |
461 | #if 0 /* disabled in v1.11 (font library search) */ | ||
442 | if (cmp_Version(&upgradedFromAppVersion, &(iVersion){ 1, 8, 0 }) < 0) { | 462 | if (cmp_Version(&upgradedFromAppVersion, &(iVersion){ 1, 8, 0 }) < 0) { |
443 | #if !defined (iPlatformAppleMobile) && !defined (iPlatformAndroidMobile) | 463 | #if !defined (iPlatformAppleMobile) && !defined (iPlatformAndroidMobile) |
444 | /* When upgrading to v1.8.0, the old hardcoded font library is gone and that means | 464 | /* When upgrading to v1.8.0, the old hardcoded font library is gone and that means |
@@ -448,6 +468,7 @@ static void loadPrefs_App_(iApp *d) { | |||
448 | postCommand_App("~fontpack.suggest.classic"); | 468 | postCommand_App("~fontpack.suggest.classic"); |
449 | #endif | 469 | #endif |
450 | } | 470 | } |
471 | #endif | ||
451 | #if !defined (LAGRANGE_ENABLE_CUSTOM_FRAME) | 472 | #if !defined (LAGRANGE_ENABLE_CUSTOM_FRAME) |
452 | d->prefs.customFrame = iFalse; | 473 | d->prefs.customFrame = iFalse; |
453 | #endif | 474 | #endif |
@@ -470,10 +491,36 @@ static const char *magicTabDocument_App_ = "tabd"; | |||
470 | static const char *magicSidebar_App_ = "side"; | 491 | static const char *magicSidebar_App_ = "side"; |
471 | 492 | ||
472 | enum iDocumentStateFlag { | 493 | enum iDocumentStateFlag { |
473 | current_DocumentStateFlag = iBit(1), | 494 | current_DocumentStateFlag = iBit(1), |
474 | rootIndex1_DocumentStateFlag = iBit(2) | 495 | rootIndex1_DocumentStateFlag = iBit(2), |
496 | }; | ||
497 | |||
498 | enum iWindowStateFlag { | ||
499 | current_WindowStateFlag = iBit(9), | ||
475 | }; | 500 | }; |
476 | 501 | ||
502 | static iRect initialWindowRect_App_(const iApp *d, size_t windowIndex) { | ||
503 | if (windowIndex < size_Array(&d->initialWindowRects)) { | ||
504 | return constValue_Array(&d->initialWindowRects, windowIndex, iRect); | ||
505 | } | ||
506 | /* The default window rectangle. */ | ||
507 | iRect rect = init_Rect(-1, -1, 900, 560); | ||
508 | #if defined (iPlatformMsys) | ||
509 | /* Must scale by UI scaling factor. */ | ||
510 | mulfv_I2(&rect.size, desktopDPI_Win32()); | ||
511 | #endif | ||
512 | #if defined (iPlatformLinux) && !defined (iPlatformAndroid) | ||
513 | /* Scale by the primary (?) monitor DPI. */ | ||
514 | if (isRunningUnderWindowSystem_App()) { | ||
515 | float vdpi; | ||
516 | SDL_GetDisplayDPI(0, NULL, NULL, &vdpi); | ||
517 | const float factor = vdpi / 96.0f; | ||
518 | mulfv_I2(&rect.size, iMax(factor, 1.0f)); | ||
519 | } | ||
520 | #endif | ||
521 | return rect; | ||
522 | } | ||
523 | |||
477 | static iBool loadState_App_(iApp *d) { | 524 | static iBool loadState_App_(iApp *d) { |
478 | iUnused(d); | 525 | iUnused(d); |
479 | const char *oldPath = concatPath_CStr(dataDir_App_(), oldStateFileName_App_); | 526 | const char *oldPath = concatPath_CStr(dataDir_App_(), oldStateFileName_App_); |
@@ -494,19 +541,49 @@ static iBool loadState_App_(iApp *d) { | |||
494 | } | 541 | } |
495 | setVersion_Stream(stream_File(f), version); | 542 | setVersion_Stream(stream_File(f), version); |
496 | /* Window state. */ | 543 | /* Window state. */ |
497 | iDocumentWidget *doc = NULL; | 544 | iDeclareType(CurrentTabs); |
498 | iDocumentWidget *current[2] = { NULL, NULL }; | 545 | struct Impl_CurrentTabs { |
499 | iBool isFirstTab[2] = { iTrue, iTrue }; | 546 | iDocumentWidget *currentTab[2]; /* for each root */ |
547 | }; | ||
548 | int numWins = 0; | ||
549 | iMainWindow * win = NULL; | ||
550 | iMainWindow * currentWin = d->window; | ||
551 | iArray * currentTabs; /* two per window (per root per window) */ | ||
552 | iBool isFirstTab[2]; | ||
553 | currentTabs = collectNew_Array(sizeof(iCurrentTabs)); | ||
500 | while (!atEnd_File(f)) { | 554 | while (!atEnd_File(f)) { |
501 | readData_File(f, 4, magic); | 555 | readData_File(f, 4, magic); |
502 | if (!memcmp(magic, magicWindow_App_, 4)) { | 556 | if (!memcmp(magic, magicWindow_App_, 4)) { |
503 | const int splitMode = read32_File(f); | 557 | numWins++; |
504 | const int keyRoot = read32_File(f); | 558 | const int splitMode = read32_File(f); |
505 | d->window->pendingSplitMode = splitMode; | 559 | const int winState = read32_File(f); |
506 | setSplitMode_MainWindow(d->window, splitMode | noEvents_WindowSplit); | 560 | const int keyRoot = (winState & 1); |
507 | d->window->base.keyRoot = d->window->base.roots[keyRoot]; | 561 | const iBool isCurrent = (winState & current_WindowStateFlag) != 0; |
562 | // printf("[State] '%.4s' split:%d state:%x\n", magic, splitMode, winState); | ||
563 | if (numWins == 1) { | ||
564 | win = d->window; | ||
565 | } | ||
566 | else { | ||
567 | win = new_MainWindow(initialWindowRect_App_(d, numWins - 1)); | ||
568 | addWindow_App(win); | ||
569 | } | ||
570 | pushBack_Array(currentTabs, &(iCurrentTabs){ { NULL, NULL } }); | ||
571 | isFirstTab[0] = isFirstTab[1] = iTrue; | ||
572 | if (isCurrent) { | ||
573 | currentWin = win; | ||
574 | } | ||
575 | setCurrent_Window(win); | ||
576 | setCurrent_Root(NULL); | ||
577 | win->pendingSplitMode = splitMode; | ||
578 | setSplitMode_MainWindow(win, splitMode | noEvents_WindowSplit); | ||
579 | win->base.keyRoot = win->base.roots[keyRoot]; | ||
508 | } | 580 | } |
509 | else if (!memcmp(magic, magicSidebar_App_, 4)) { | 581 | else if (!memcmp(magic, magicSidebar_App_, 4)) { |
582 | if (!win) { | ||
583 | printf("%s: missing window\n", cstr_String(path_File(f))); | ||
584 | setCurrent_Root(NULL); | ||
585 | return iFalse; | ||
586 | } | ||
510 | const uint16_t bits = readU16_File(f); | 587 | const uint16_t bits = readU16_File(f); |
511 | const uint8_t modes = readU8_File(f); | 588 | const uint8_t modes = readU8_File(f); |
512 | const float widths[2] = { | 589 | const float widths[2] = { |
@@ -523,7 +600,7 @@ static iBool loadState_App_(iApp *d) { | |||
523 | } | 600 | } |
524 | const uint8_t rootIndex = bits & 0xff; | 601 | const uint8_t rootIndex = bits & 0xff; |
525 | const uint8_t flags = bits >> 8; | 602 | const uint8_t flags = bits >> 8; |
526 | iRoot *root = d->window->base.roots[rootIndex]; | 603 | iRoot *root = win->base.roots[rootIndex]; |
527 | if (root) { | 604 | if (root) { |
528 | iSidebarWidget *sidebar = findChild_Widget(root->widget, "sidebar"); | 605 | iSidebarWidget *sidebar = findChild_Widget(root->widget, "sidebar"); |
529 | iSidebarWidget *sidebar2 = findChild_Widget(root->widget, "sidebar2"); | 606 | iSidebarWidget *sidebar2 = findChild_Widget(root->widget, "sidebar2"); |
@@ -546,12 +623,18 @@ static iBool loadState_App_(iApp *d) { | |||
546 | } | 623 | } |
547 | } | 624 | } |
548 | else if (!memcmp(magic, magicTabDocument_App_, 4)) { | 625 | else if (!memcmp(magic, magicTabDocument_App_, 4)) { |
626 | if (!win) { | ||
627 | printf("%s: missing window\n", cstr_String(path_File(f))); | ||
628 | setCurrent_Root(NULL); | ||
629 | return iFalse; | ||
630 | } | ||
549 | const int8_t flags = read8_File(f); | 631 | const int8_t flags = read8_File(f); |
550 | int rootIndex = flags & rootIndex1_DocumentStateFlag ? 1 : 0; | 632 | int rootIndex = flags & rootIndex1_DocumentStateFlag ? 1 : 0; |
551 | if (rootIndex > numRoots_Window(as_Window(d->window)) - 1) { | 633 | if (rootIndex > numRoots_Window(as_Window(win)) - 1) { |
552 | rootIndex = 0; | 634 | rootIndex = 0; |
553 | } | 635 | } |
554 | setCurrent_Root(d->window->base.roots[rootIndex]); | 636 | setCurrent_Root(win->base.roots[rootIndex]); |
637 | iDocumentWidget *doc; | ||
555 | if (isFirstTab[rootIndex]) { | 638 | if (isFirstTab[rootIndex]) { |
556 | isFirstTab[rootIndex] = iFalse; | 639 | isFirstTab[rootIndex] = iFalse; |
557 | /* There is one pre-created tab in each root. */ | 640 | /* There is one pre-created tab in each root. */ |
@@ -561,7 +644,7 @@ static iBool loadState_App_(iApp *d) { | |||
561 | doc = newTab_App(NULL, iFalse /* no switching */); | 644 | doc = newTab_App(NULL, iFalse /* no switching */); |
562 | } | 645 | } |
563 | if (flags & current_DocumentStateFlag) { | 646 | if (flags & current_DocumentStateFlag) { |
564 | current[rootIndex] = doc; | 647 | value_Array(currentTabs, numWins - 1, iCurrentTabs).currentTab[rootIndex] = doc; |
565 | } | 648 | } |
566 | deserializeState_DocumentWidget(doc, stream_File(f)); | 649 | deserializeState_DocumentWidget(doc, stream_File(f)); |
567 | doc = NULL; | 650 | doc = NULL; |
@@ -572,12 +655,23 @@ static iBool loadState_App_(iApp *d) { | |||
572 | return iFalse; | 655 | return iFalse; |
573 | } | 656 | } |
574 | } | 657 | } |
575 | if (d->window->splitMode) { | 658 | iForEach(Array, i, currentTabs) { |
576 | /* Update root placement. */ | 659 | const iCurrentTabs *cur = i.value; |
577 | resize_MainWindow(d->window, -1, -1); | 660 | win = at_PtrArray(&d->mainWindows, index_ArrayIterator(&i)); |
661 | for (size_t j = 0; j < 2; ++j) { | ||
662 | postCommandf_Root(win->base.roots[j], "tabs.switch page:%p", cur->currentTab[j]); | ||
663 | } | ||
664 | if (win->splitMode) { | ||
665 | /* Update root placement. */ | ||
666 | resize_MainWindow(win, -1, -1); | ||
667 | } | ||
668 | // postCommand_Root(win->base.roots[0], "window.unfreeze"); | ||
669 | win->isDrawFrozen = iFalse; | ||
670 | SDL_ShowWindow(win->base.win); | ||
578 | } | 671 | } |
579 | iForIndices(i, current) { | 672 | if (numWindows_App() > 1) { |
580 | postCommandf_Root(NULL, "tabs.switch page:%p", current[i]); | 673 | SDL_RaiseWindow(currentWin->base.win); |
674 | setActiveWindow_App(currentWin); | ||
581 | } | 675 | } |
582 | setCurrent_Root(NULL); | 676 | setCurrent_Root(NULL); |
583 | return iTrue; | 677 | return iTrue; |
@@ -588,7 +682,6 @@ static iBool loadState_App_(iApp *d) { | |||
588 | static void saveState_App_(const iApp *d) { | 682 | static void saveState_App_(const iApp *d) { |
589 | iUnused(d); | 683 | iUnused(d); |
590 | trimCache_App(); | 684 | trimCache_App(); |
591 | iMainWindow *win = d->window; | ||
592 | /* UI state is saved in binary because it is quite complex (e.g., | 685 | /* UI state is saved in binary because it is quite complex (e.g., |
593 | navigation history, cached content) and depends closely on the widget | 686 | navigation history, cached content) and depends closely on the widget |
594 | tree. The data is largely not reorderable and should not be modified | 687 | tree. The data is largely not reorderable and should not be modified |
@@ -597,43 +690,48 @@ static void saveState_App_(const iApp *d) { | |||
597 | if (open_File(f, writeOnly_FileMode)) { | 690 | if (open_File(f, writeOnly_FileMode)) { |
598 | writeData_File(f, magicState_App_, 4); | 691 | writeData_File(f, magicState_App_, 4); |
599 | writeU32_File(f, latest_FileVersion); /* version */ | 692 | writeU32_File(f, latest_FileVersion); /* version */ |
600 | /* Begin with window state. */ { | 693 | iConstForEach(PtrArray, winIter, &d->mainWindows) { |
601 | writeData_File(f, magicWindow_App_, 4); | 694 | const iMainWindow *win = winIter.ptr; |
602 | writeU32_File(f, win->splitMode); | 695 | setCurrent_Window(winIter.ptr); |
603 | writeU32_File(f, win->base.keyRoot == win->base.roots[0] ? 0 : 1); | 696 | /* Window state. */ { |
604 | } | 697 | writeData_File(f, magicWindow_App_, 4); |
605 | /* State of UI elements. */ { | 698 | writeU32_File(f, win->splitMode); |
606 | iForIndices(i, win->base.roots) { | 699 | writeU32_File(f, (win->base.keyRoot == win->base.roots[0] ? 0 : 1) | |
607 | const iRoot *root = win->base.roots[i]; | 700 | (win == d->window ? current_WindowStateFlag : 0)); |
608 | if (root) { | 701 | } |
609 | writeData_File(f, magicSidebar_App_, 4); | 702 | /* State of UI elements. */ { |
610 | const iSidebarWidget *sidebar = findChild_Widget(root->widget, "sidebar"); | 703 | iForIndices(i, win->base.roots) { |
611 | const iSidebarWidget *sidebar2 = findChild_Widget(root->widget, "sidebar2"); | 704 | const iRoot *root = win->base.roots[i]; |
612 | writeU16_File(f, i | | 705 | if (root) { |
613 | (isVisible_Widget(sidebar) ? 0x100 : 0) | | 706 | writeData_File(f, magicSidebar_App_, 4); |
614 | (isVisible_Widget(sidebar2) ? 0x200 : 0) | | 707 | const iSidebarWidget *sidebar = findChild_Widget(root->widget, "sidebar"); |
615 | (feedsMode_SidebarWidget(sidebar) == unread_FeedsMode ? 0x400 : 0) | | 708 | const iSidebarWidget *sidebar2 = findChild_Widget(root->widget, "sidebar2"); |
616 | (feedsMode_SidebarWidget(sidebar2) == unread_FeedsMode ? 0x800 : 0)); | 709 | writeU16_File(f, i | |
617 | writeU8_File(f, | 710 | (isVisible_Widget(sidebar) ? 0x100 : 0) | |
618 | mode_SidebarWidget(sidebar) | | 711 | (isVisible_Widget(sidebar2) ? 0x200 : 0) | |
619 | (mode_SidebarWidget(sidebar2) << 4)); | 712 | (feedsMode_SidebarWidget(sidebar) == unread_FeedsMode ? 0x400 : 0) | |
620 | writef_Stream(stream_File(f), width_SidebarWidget(sidebar)); | 713 | (feedsMode_SidebarWidget(sidebar2) == unread_FeedsMode ? 0x800 : 0)); |
621 | writef_Stream(stream_File(f), width_SidebarWidget(sidebar2)); | 714 | writeU8_File(f, |
622 | serialize_IntSet(closedFolders_SidebarWidget(sidebar), stream_File(f)); | 715 | mode_SidebarWidget(sidebar) | |
623 | serialize_IntSet(closedFolders_SidebarWidget(sidebar2), stream_File(f)); | 716 | (mode_SidebarWidget(sidebar2) << 4)); |
717 | writef_Stream(stream_File(f), width_SidebarWidget(sidebar)); | ||
718 | writef_Stream(stream_File(f), width_SidebarWidget(sidebar2)); | ||
719 | serialize_IntSet(closedFolders_SidebarWidget(sidebar), stream_File(f)); | ||
720 | serialize_IntSet(closedFolders_SidebarWidget(sidebar2), stream_File(f)); | ||
721 | } | ||
624 | } | 722 | } |
625 | } | 723 | } |
626 | } | 724 | iConstForEach(ObjectList, i, iClob(listDocuments_App(NULL))) { |
627 | iConstForEach(ObjectList, i, iClob(listDocuments_App(NULL))) { | 725 | iAssert(isInstance_Object(i.object, &Class_DocumentWidget)); |
628 | iAssert(isInstance_Object(i.object, &Class_DocumentWidget)); | 726 | const iWidget *widget = constAs_Widget(i.object); |
629 | const iWidget *widget = constAs_Widget(i.object); | 727 | writeData_File(f, magicTabDocument_App_, 4); |
630 | writeData_File(f, magicTabDocument_App_, 4); | 728 | int8_t flags = (document_Root(widget->root) == i.object ? current_DocumentStateFlag : 0); |
631 | int8_t flags = (document_Root(widget->root) == i.object ? current_DocumentStateFlag : 0); | 729 | if (widget->root == win->base.roots[1]) { |
632 | if (widget->root == win->base.roots[1]) { | 730 | flags |= rootIndex1_DocumentStateFlag; |
633 | flags |= rootIndex1_DocumentStateFlag; | 731 | } |
732 | write8_File(f, flags); | ||
733 | serializeState_DocumentWidget(i.object, stream_File(f)); | ||
634 | } | 734 | } |
635 | write8_File(f, flags); | ||
636 | serializeState_DocumentWidget(i.object, stream_File(f)); | ||
637 | } | 735 | } |
638 | iRelease(f); | 736 | iRelease(f); |
639 | } | 737 | } |
@@ -760,6 +858,7 @@ static void init_App_(iApp *d, int argc, char **argv) { | |||
760 | d->isDarkSystemTheme = iTrue; /* will be updated by system later on, if supported */ | 858 | d->isDarkSystemTheme = iTrue; /* will be updated by system later on, if supported */ |
761 | d->isSuspended = iFalse; | 859 | d->isSuspended = iFalse; |
762 | d->tempFilesPendingDeletion = new_StringSet(); | 860 | d->tempFilesPendingDeletion = new_StringSet(); |
861 | init_Array(&d->initialWindowRects, sizeof(iRect)); | ||
763 | init_CommandLine(&d->args, argc, argv); | 862 | init_CommandLine(&d->args, argc, argv); |
764 | /* Where was the app started from? We ask SDL first because the command line alone | 863 | /* Where was the app started from? We ask SDL first because the command line alone |
765 | cannot be relied on (behavior differs depending on OS). */ { | 864 | cannot be relied on (behavior differs depending on OS). */ { |
@@ -894,20 +993,6 @@ static void init_App_(iApp *d, int argc, char **argv) { | |||
894 | d->elapsedSinceLastTicker = 0; | 993 | d->elapsedSinceLastTicker = 0; |
895 | d->commandEcho = iClob(checkArgument_CommandLine(&d->args, "echo;E")) != NULL; | 994 | d->commandEcho = iClob(checkArgument_CommandLine(&d->args, "echo;E")) != NULL; |
896 | d->forceSoftwareRender = iClob(checkArgument_CommandLine(&d->args, "sw")) != NULL; | 995 | d->forceSoftwareRender = iClob(checkArgument_CommandLine(&d->args, "sw")) != NULL; |
897 | d->initialWindowRect = init_Rect(-1, -1, 900, 560); | ||
898 | #if defined (iPlatformMsys) | ||
899 | /* Must scale by UI scaling factor. */ | ||
900 | mulfv_I2(&d->initialWindowRect.size, desktopDPI_Win32()); | ||
901 | #endif | ||
902 | #if defined (iPlatformLinux) && !defined (iPlatformAndroid) | ||
903 | /* Scale by the primary (?) monitor DPI. */ | ||
904 | if (isRunningUnderWindowSystem_App()) { | ||
905 | float vdpi; | ||
906 | SDL_GetDisplayDPI(0, NULL, NULL, &vdpi); | ||
907 | const float factor = vdpi / 96.0f; | ||
908 | mulfv_I2(&d->initialWindowRect.size, iMax(factor, 1.0f)); | ||
909 | } | ||
910 | #endif | ||
911 | init_Prefs(&d->prefs); | 996 | init_Prefs(&d->prefs); |
912 | init_SiteSpec(dataDir_App_()); | 997 | init_SiteSpec(dataDir_App_()); |
913 | setCStr_String(&d->prefs.strings[downloadDir_PrefsString], downloadDir_App_()); | 998 | setCStr_String(&d->prefs.strings[downloadDir_PrefsString], downloadDir_App_()); |
@@ -929,21 +1014,30 @@ static void init_App_(iApp *d, int argc, char **argv) { | |||
929 | init_Fonts(dataDir_App_()); | 1014 | init_Fonts(dataDir_App_()); |
930 | loadPalette_Color(dataDir_App_()); | 1015 | loadPalette_Color(dataDir_App_()); |
931 | setThemePalette_Color(d->prefs.theme); /* default UI colors */ | 1016 | setThemePalette_Color(d->prefs.theme); /* default UI colors */ |
932 | loadPrefs_App_(d); | 1017 | /* Initial window rectangle of the first window. */ { |
1018 | iAssert(isEmpty_Array(&d->initialWindowRects)); | ||
1019 | const iRect winRect = initialWindowRect_App_(d, 0); /* calculated */ | ||
1020 | resize_Array(&d->initialWindowRects, 1); | ||
1021 | set_Array(&d->initialWindowRects, 0, &winRect); | ||
1022 | } | ||
1023 | loadPrefs_App_(d); | ||
933 | updateActive_Fonts(); | 1024 | updateActive_Fonts(); |
934 | load_Keys(dataDir_App_()); | 1025 | load_Keys(dataDir_App_()); |
1026 | iRect *winRect0 = at_Array(&d->initialWindowRects, 0); | ||
935 | /* See if the user wants to override the window size. */ { | 1027 | /* See if the user wants to override the window size. */ { |
936 | iCommandLineArg *arg = iClob(checkArgument_CommandLine(&d->args, windowWidth_CommandLineOption)); | 1028 | iCommandLineArg *arg = iClob(checkArgument_CommandLine(&d->args, windowWidth_CommandLineOption)); |
937 | if (arg) { | 1029 | if (arg) { |
938 | d->initialWindowRect.size.x = toInt_String(value_CommandLineArg(arg, 0)); | 1030 | winRect0->size.x = toInt_String(value_CommandLineArg(arg, 0)); |
939 | } | 1031 | } |
940 | arg = iClob(checkArgument_CommandLine(&d->args, windowHeight_CommandLineOption)); | 1032 | arg = iClob(checkArgument_CommandLine(&d->args, windowHeight_CommandLineOption)); |
941 | if (arg) { | 1033 | if (arg) { |
942 | d->initialWindowRect.size.y = toInt_String(value_CommandLineArg(arg, 0)); | 1034 | winRect0->size.y = toInt_String(value_CommandLineArg(arg, 0)); |
943 | } | 1035 | } |
944 | } | 1036 | } |
1037 | init_PtrArray(&d->mainWindows); | ||
945 | init_PtrArray(&d->popupWindows); | 1038 | init_PtrArray(&d->popupWindows); |
946 | d->window = new_MainWindow(d->initialWindowRect); | 1039 | d->window = new_MainWindow(*winRect0); /* first window is always created */ |
1040 | addWindow_App(d->window); | ||
947 | load_Visited(d->visited, dataDir_App_()); | 1041 | load_Visited(d->visited, dataDir_App_()); |
948 | load_Bookmarks(d->bookmarks, dataDir_App_()); | 1042 | load_Bookmarks(d->bookmarks, dataDir_App_()); |
949 | load_MimeHooks(d->mimehooks, dataDir_App_()); | 1043 | load_MimeHooks(d->mimehooks, dataDir_App_()); |
@@ -968,8 +1062,8 @@ static void init_App_(iApp *d, int argc, char **argv) { | |||
968 | } | 1062 | } |
969 | postCommand_App("~navbar.actions.changed"); | 1063 | postCommand_App("~navbar.actions.changed"); |
970 | postCommand_App("~toolbar.actions.changed"); | 1064 | postCommand_App("~toolbar.actions.changed"); |
971 | postCommand_Root(NULL, "~window.unfreeze"); | 1065 | postCommand_App("~window.unfreeze"); |
972 | postCommand_Root(NULL, "font.reset"); | 1066 | postCommand_App("font.reset"); |
973 | d->autoReloadTimer = SDL_AddTimer(60 * 1000, postAutoReloadCommand_App_, NULL); | 1067 | d->autoReloadTimer = SDL_AddTimer(60 * 1000, postAutoReloadCommand_App_, NULL); |
974 | postCommand_Root(NULL, "document.autoreload"); | 1068 | postCommand_Root(NULL, "document.autoreload"); |
975 | #if defined (LAGRANGE_ENABLE_IDLE_SLEEP) | 1069 | #if defined (LAGRANGE_ENABLE_IDLE_SLEEP) |
@@ -1008,7 +1102,11 @@ static void deinit_App(iApp *d) { | |||
1008 | SDL_RemoveTimer(d->autoReloadTimer); | 1102 | SDL_RemoveTimer(d->autoReloadTimer); |
1009 | saveState_App_(d); | 1103 | saveState_App_(d); |
1010 | savePrefs_App_(d); | 1104 | savePrefs_App_(d); |
1011 | delete_MainWindow(d->window); | 1105 | iReverseForEach(PtrArray, j, &d->mainWindows) { |
1106 | delete_MainWindow(j.ptr); | ||
1107 | } | ||
1108 | iAssert(isEmpty_PtrArray(&d->mainWindows)); | ||
1109 | deinit_PtrArray(&d->mainWindows); | ||
1012 | d->window = NULL; | 1110 | d->window = NULL; |
1013 | deinit_Feeds(); | 1111 | deinit_Feeds(); |
1014 | save_Keys(dataDir_App_()); | 1112 | save_Keys(dataDir_App_()); |
@@ -1023,7 +1121,6 @@ static void deinit_App(iApp *d) { | |||
1023 | delete_GmCerts(d->certs); | 1121 | delete_GmCerts(d->certs); |
1024 | save_MimeHooks(d->mimehooks); | 1122 | save_MimeHooks(d->mimehooks); |
1025 | delete_MimeHooks(d->mimehooks); | 1123 | delete_MimeHooks(d->mimehooks); |
1026 | d->window = NULL; | ||
1027 | deinit_CommandLine(&d->args); | 1124 | deinit_CommandLine(&d->args); |
1028 | iRelease(d->launchCommands); | 1125 | iRelease(d->launchCommands); |
1029 | delete_String(d->execPath); | 1126 | delete_String(d->execPath); |
@@ -1037,7 +1134,8 @@ static void deinit_App(iApp *d) { | |||
1037 | /* Delete all temporary files created while running. */ | 1134 | /* Delete all temporary files created while running. */ |
1038 | iConstForEach(StringSet, tmp, d->tempFilesPendingDeletion) { | 1135 | iConstForEach(StringSet, tmp, d->tempFilesPendingDeletion) { |
1039 | remove(cstr_String(tmp.value)); | 1136 | remove(cstr_String(tmp.value)); |
1040 | } | 1137 | } |
1138 | deinit_Array(&d->initialWindowRects); | ||
1041 | iRelease(d->tempFilesPendingDeletion); | 1139 | iRelease(d->tempFilesPendingDeletion); |
1042 | } | 1140 | } |
1043 | 1141 | ||
@@ -1282,10 +1380,23 @@ static iPtrArray *listWindows_App_(const iApp *d, iPtrArray *windows) { | |||
1282 | iReverseConstForEach(PtrArray, i, &d->popupWindows) { | 1380 | iReverseConstForEach(PtrArray, i, &d->popupWindows) { |
1283 | pushBack_PtrArray(windows, i.ptr); | 1381 | pushBack_PtrArray(windows, i.ptr); |
1284 | } | 1382 | } |
1285 | pushBack_PtrArray(windows, d->window); | 1383 | if (d->window) { |
1384 | pushBack_PtrArray(windows, d->window); | ||
1385 | } | ||
1386 | iConstForEach(PtrArray, j, &d->mainWindows) { | ||
1387 | if (j.ptr != d->window) { | ||
1388 | pushBack_PtrArray(windows, j.ptr); | ||
1389 | } | ||
1390 | } | ||
1286 | return windows; | 1391 | return windows; |
1287 | } | 1392 | } |
1288 | 1393 | ||
1394 | iPtrArray *listWindows_App(void) { | ||
1395 | iPtrArray *wins = new_PtrArray(); | ||
1396 | listWindows_App_(&app_, wins); | ||
1397 | return wins; | ||
1398 | } | ||
1399 | |||
1289 | void processEvents_App(enum iAppEventMode eventMode) { | 1400 | void processEvents_App(enum iAppEventMode eventMode) { |
1290 | iApp *d = &app_; | 1401 | iApp *d = &app_; |
1291 | iRoot *oldCurrentRoot = current_Root(); /* restored afterwards */ | 1402 | iRoot *oldCurrentRoot = current_Root(); /* restored afterwards */ |
@@ -1365,6 +1476,10 @@ void processEvents_App(enum iAppEventMode eventMode) { | |||
1365 | dispatchCommands_Periodic(&d->periodic); | 1476 | dispatchCommands_Periodic(&d->periodic); |
1366 | continue; | 1477 | continue; |
1367 | } | 1478 | } |
1479 | if (ev.type == SDL_USEREVENT && ev.user.code == releaseObject_UserEventCode) { | ||
1480 | iRelease(ev.user.data1); | ||
1481 | continue; | ||
1482 | } | ||
1368 | if (ev.type == SDL_USEREVENT && ev.user.code == refresh_UserEventCode) { | 1483 | if (ev.type == SDL_USEREVENT && ev.user.code == refresh_UserEventCode) { |
1369 | gotRefresh = iTrue; | 1484 | gotRefresh = iTrue; |
1370 | continue; | 1485 | continue; |
@@ -1470,6 +1585,7 @@ void processEvents_App(enum iAppEventMode eventMode) { | |||
1470 | window->lastHover = window->hover; | 1585 | window->lastHover = window->hover; |
1471 | wasUsed = processEvent_Window(window, &ev); | 1586 | wasUsed = processEvent_Window(window, &ev); |
1472 | if (ev.type == SDL_MOUSEMOTION || ev.type == SDL_MOUSEBUTTONDOWN) { | 1587 | if (ev.type == SDL_MOUSEMOTION || ev.type == SDL_MOUSEBUTTONDOWN) { |
1588 | /* Only offered to the frontmost window. */ | ||
1473 | break; | 1589 | break; |
1474 | } | 1590 | } |
1475 | if (wasUsed) break; | 1591 | if (wasUsed) break; |
@@ -1563,6 +1679,9 @@ static void runTickers_App_(iApp *d) { | |||
1563 | iConstForEach(Array, i, &pending->values) { | 1679 | iConstForEach(Array, i, &pending->values) { |
1564 | const iTicker *ticker = i.value; | 1680 | const iTicker *ticker = i.value; |
1565 | if (ticker->callback) { | 1681 | if (ticker->callback) { |
1682 | if (ticker->root) { | ||
1683 | setCurrent_Window(ticker->root->window); | ||
1684 | } | ||
1566 | setCurrent_Root(ticker->root); /* root might be NULL */ | 1685 | setCurrent_Root(ticker->root); /* root might be NULL */ |
1567 | ticker->callback(ticker->context); | 1686 | ticker->callback(ticker->context); |
1568 | } | 1687 | } |
@@ -1755,11 +1874,11 @@ void postCommand_Root(iRoot *d, const char *command) { | |||
1755 | } | 1874 | } |
1756 | SDL_Event ev = { .type = SDL_USEREVENT }; | 1875 | SDL_Event ev = { .type = SDL_USEREVENT }; |
1757 | ev.user.code = command_UserEventCode; | 1876 | ev.user.code = command_UserEventCode; |
1758 | /*ev.user.windowID = id_Window(get_Window());*/ | ||
1759 | ev.user.data1 = strdup(command); | 1877 | ev.user.data1 = strdup(command); |
1760 | ev.user.data2 = d; /* all events are root-specific */ | 1878 | ev.user.data2 = d; /* all events are root-specific */ |
1879 | ev.user.windowID = d ? id_Window(d->window) : 0; /* root-specific means window-specific */ | ||
1761 | SDL_PushEvent(&ev); | 1880 | SDL_PushEvent(&ev); |
1762 | iWindow *win = get_Window(); | 1881 | iWindow *win = d ? d->window : NULL; |
1763 | #if defined (iPlatformAndroid) | 1882 | #if defined (iPlatformAndroid) |
1764 | SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "%s[command] {%d} %s", | 1883 | SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "%s[command] {%d} %s", |
1765 | app_.isLoadingPrefs ? "[Prefs] " : "", | 1884 | app_.isLoadingPrefs ? "[Prefs] " : "", |
@@ -1767,10 +1886,17 @@ void postCommand_Root(iRoot *d, const char *command) { | |||
1767 | command); | 1886 | command); |
1768 | #else | 1887 | #else |
1769 | if (app_.commandEcho) { | 1888 | if (app_.commandEcho) { |
1770 | printf("%s[command] {%d} %s\n", | 1889 | const int windowIndex = |
1771 | app_.isLoadingPrefs ? "[Prefs] " : "", | 1890 | win && type_Window(win) == main_WindowType ? windowIndex_App(as_MainWindow(win)) + 1 : 0; |
1772 | (d == NULL || win == NULL ? 0 : d == win->roots[0] ? 1 : 2), | 1891 | printf("%s%s[command] {%d:%d} %s\n", |
1773 | command); fflush(stdout); | 1892 | !app_.isFinishedLaunching ? "<Ln> " : "", |
1893 | app_.isLoadingPrefs ? "<Pr> " : "", | ||
1894 | windowIndex, | ||
1895 | (d == NULL || win == NULL ? 0 | ||
1896 | : d == win->roots[0] ? 1 | ||
1897 | : 2), | ||
1898 | command); | ||
1899 | fflush(stdout); | ||
1774 | } | 1900 | } |
1775 | #endif | 1901 | #endif |
1776 | } | 1902 | } |
@@ -1835,6 +1961,41 @@ void removeTicker_App(iTickerFunc ticker, iAny *context) { | |||
1835 | remove_SortedArray(&d->tickers, &(iTicker){ context, NULL, ticker }); | 1961 | remove_SortedArray(&d->tickers, &(iTicker){ context, NULL, ticker }); |
1836 | } | 1962 | } |
1837 | 1963 | ||
1964 | void addWindow_App(iMainWindow *win) { | ||
1965 | iApp *d = &app_; | ||
1966 | pushBack_PtrArray(&d->mainWindows, win); | ||
1967 | } | ||
1968 | |||
1969 | void removeWindow_App(iMainWindow *win) { | ||
1970 | iApp *d = &app_; | ||
1971 | removeOne_PtrArray(&d->mainWindows, win); | ||
1972 | } | ||
1973 | |||
1974 | size_t numWindows_App(void) { | ||
1975 | return size_PtrArray(&app_.mainWindows); | ||
1976 | } | ||
1977 | |||
1978 | size_t windowIndex_App(const iMainWindow *win) { | ||
1979 | return indexOf_PtrArray(&app_.mainWindows, win); | ||
1980 | } | ||
1981 | |||
1982 | iMainWindow *newMainWindow_App(void) { | ||
1983 | iApp *d = &app_; | ||
1984 | iMainWindow *win = new_MainWindow(initialWindowRect_App_(d, size_PtrArray(&d->mainWindows))); | ||
1985 | addWindow_App(win); | ||
1986 | return win; | ||
1987 | } | ||
1988 | |||
1989 | const iPtrArray *mainWindows_App(void) { | ||
1990 | return &app_.mainWindows; | ||
1991 | } | ||
1992 | |||
1993 | void setActiveWindow_App(iMainWindow *win) { | ||
1994 | iApp *d = &app_; | ||
1995 | d->window = win; | ||
1996 | printf("Active window: %p\n", win); fflush(stdout); | ||
1997 | } | ||
1998 | |||
1838 | void addPopup_App(iWindow *popup) { | 1999 | void addPopup_App(iWindow *popup) { |
1839 | iApp *d = &app_; | 2000 | iApp *d = &app_; |
1840 | pushBack_PtrArray(&d->popupWindows, popup); | 2001 | pushBack_PtrArray(&d->popupWindows, popup); |
@@ -1970,6 +2131,8 @@ static iBool handlePrefsCommands_(iWidget *d, const char *cmd) { | |||
1970 | toInt_String(text_InputWidget(findChild_Widget(d, "prefs.cachesize")))); | 2131 | toInt_String(text_InputWidget(findChild_Widget(d, "prefs.cachesize")))); |
1971 | postCommandf_App("memorysize.set arg:%d", | 2132 | postCommandf_App("memorysize.set arg:%d", |
1972 | toInt_String(text_InputWidget(findChild_Widget(d, "prefs.memorysize")))); | 2133 | toInt_String(text_InputWidget(findChild_Widget(d, "prefs.memorysize")))); |
2134 | postCommandf_App("urlsize.set arg:%d", | ||
2135 | toInt_String(text_InputWidget(findChild_Widget(d, "prefs.urlsize")))); | ||
1973 | postCommandf_App("ca.file path:%s", | 2136 | postCommandf_App("ca.file path:%s", |
1974 | cstrText_InputWidget(findChild_Widget(d, "prefs.ca.file"))); | 2137 | cstrText_InputWidget(findChild_Widget(d, "prefs.ca.file"))); |
1975 | postCommandf_App("ca.path path:%s", | 2138 | postCommandf_App("ca.path path:%s", |
@@ -2111,6 +2274,22 @@ iDocumentWidget *newTab_App(const iDocumentWidget *duplicateOf, iBool switchToNe | |||
2111 | return doc; | 2274 | return doc; |
2112 | } | 2275 | } |
2113 | 2276 | ||
2277 | void closeWindow_App(iMainWindow *win) { | ||
2278 | iApp *d = &app_; | ||
2279 | delete_MainWindow(win); | ||
2280 | if (d->window == win) { | ||
2281 | /* Activate another window. */ | ||
2282 | iForEach(PtrArray, i, &d->mainWindows) { | ||
2283 | if (i.ptr != d->window) { | ||
2284 | SDL_RaiseWindow(i.ptr); | ||
2285 | setActiveWindow_App(i.ptr); | ||
2286 | setCurrent_Window(i.ptr); | ||
2287 | break; | ||
2288 | } | ||
2289 | } | ||
2290 | } | ||
2291 | } | ||
2292 | |||
2114 | static iBool handleIdentityCreationCommands_(iWidget *dlg, const char *cmd) { | 2293 | static iBool handleIdentityCreationCommands_(iWidget *dlg, const char *cmd) { |
2115 | iApp *d = &app_; | 2294 | iApp *d = &app_; |
2116 | if (equal_Command(cmd, "ident.showmore")) { | 2295 | if (equal_Command(cmd, "ident.showmore")) { |
@@ -2257,6 +2436,13 @@ void resetFonts_App(void) { | |||
2257 | } | 2436 | } |
2258 | } | 2437 | } |
2259 | 2438 | ||
2439 | void availableFontsChanged_App(void) { | ||
2440 | iApp *d = &app_; | ||
2441 | iConstForEach(PtrArray, win, listWindows_App_(d, collectNew_PtrArray())) { | ||
2442 | resetMissing_Text(text_Window(win.ptr)); | ||
2443 | } | ||
2444 | } | ||
2445 | |||
2260 | static void invalidateCachedDocuments_App_(void) { | 2446 | static void invalidateCachedDocuments_App_(void) { |
2261 | iForEach(ObjectList, i, iClob(listDocuments_App(NULL))) { | 2447 | iForEach(ObjectList, i, iClob(listDocuments_App(NULL))) { |
2262 | invalidateCachedLayout_History(history_DocumentWidget(i.object)); | 2448 | invalidateCachedLayout_History(history_DocumentWidget(i.object)); |
@@ -2274,6 +2460,7 @@ iBool handleCommand_App(const char *cmd) { | |||
2274 | suffixPtr_Command(cmd, "where"))); | 2460 | suffixPtr_Command(cmd, "where"))); |
2275 | return iTrue; | 2461 | return iTrue; |
2276 | } | 2462 | } |
2463 | #if 0 /* disabled in v1.11 */ | ||
2277 | else if (equal_Command(cmd, "fontpack.suggest.classic")) { | 2464 | else if (equal_Command(cmd, "fontpack.suggest.classic")) { |
2278 | /* TODO: Don't use this when system fonts are accessible. */ | 2465 | /* TODO: Don't use this when system fonts are accessible. */ |
2279 | if (!isInstalled_Fonts("classic-set") && !isInstalled_Fonts("cjk")) { | 2466 | if (!isInstalled_Fonts("classic-set") && !isInstalled_Fonts("cjk")) { |
@@ -2290,6 +2477,7 @@ iBool handleCommand_App(const char *cmd) { | |||
2290 | } | 2477 | } |
2291 | return iTrue; | 2478 | return iTrue; |
2292 | } | 2479 | } |
2480 | #endif | ||
2293 | else if (equal_Command(cmd, "prefs.changed")) { | 2481 | else if (equal_Command(cmd, "prefs.changed")) { |
2294 | savePrefs_App_(d); | 2482 | savePrefs_App_(d); |
2295 | return iTrue; | 2483 | return iTrue; |
@@ -2360,12 +2548,16 @@ iBool handleCommand_App(const char *cmd) { | |||
2360 | return iTrue; | 2548 | return iTrue; |
2361 | } | 2549 | } |
2362 | else if (equal_Command(cmd, "window.maximize")) { | 2550 | else if (equal_Command(cmd, "window.maximize")) { |
2363 | if (!argLabel_Command(cmd, "toggle")) { | 2551 | const size_t winIndex = argU32Label_Command(cmd, "index"); |
2364 | setSnap_MainWindow(d->window, maximized_WindowSnap); | 2552 | if (winIndex < size_PtrArray(&d->mainWindows)) { |
2365 | } | 2553 | iMainWindow *win = at_PtrArray(&d->mainWindows, winIndex); |
2366 | else { | 2554 | if (!argLabel_Command(cmd, "toggle")) { |
2367 | setSnap_MainWindow(d->window, snap_MainWindow(d->window) == maximized_WindowSnap ? 0 : | 2555 | setSnap_MainWindow(win, maximized_WindowSnap); |
2368 | maximized_WindowSnap); | 2556 | } |
2557 | else { | ||
2558 | setSnap_MainWindow( | ||
2559 | win, snap_MainWindow(win) == maximized_WindowSnap ? 0 : maximized_WindowSnap); | ||
2560 | } | ||
2369 | } | 2561 | } |
2370 | return iTrue; | 2562 | return iTrue; |
2371 | } | 2563 | } |
@@ -2383,24 +2575,49 @@ iBool handleCommand_App(const char *cmd) { | |||
2383 | reload_Fonts(); /* also does font cache reset, window invalidation */ | 2575 | reload_Fonts(); /* also does font cache reset, window invalidation */ |
2384 | return iTrue; | 2576 | return iTrue; |
2385 | } | 2577 | } |
2386 | #if 0 | 2578 | else if (equal_Command(cmd, "font.find")) { |
2387 | else if (equal_Command(cmd, "font.user")) { | 2579 | searchOnlineLibraryForCharacters_Fonts(string_Command(cmd, "chars")); |
2388 | const char *path = suffixPtr_Command(cmd, "path"); | 2580 | return iTrue; |
2389 | if (cmp_String(&d->prefs.symbolFontPath, path)) { | 2581 | } |
2390 | if (!isFrozen) { | 2582 | else if (equal_Command(cmd, "font.found")) { |
2391 | setFreezeDraw_MainWindow(get_MainWindow(), iTrue); | 2583 | if (hasLabel_Command(cmd, "error")) { |
2392 | } | 2584 | makeSimpleMessage_Widget("${heading.glyphfinder}", |
2393 | setCStr_String(&d->prefs.symbolFontPath, path); | 2585 | format_CStr("%d %s", |
2394 | loadUserFonts_Text(); | 2586 | argLabel_Command(cmd, "error"), |
2395 | resetFonts_App(d); | 2587 | suffixPtr_Command(cmd, "msg"))); |
2396 | if (!isFrozen) { | 2588 | return iTrue; |
2397 | postCommand_App("font.changed"); | ||
2398 | postCommand_App("window.unfreeze"); | ||
2399 | } | ||
2400 | } | 2589 | } |
2590 | iString *src = collectNew_String(); | ||
2591 | setCStr_String(src, "# ${heading.glyphfinder.results}\n\n"); | ||
2592 | iRangecc path = iNullRange; | ||
2593 | iBool isFirst = iTrue; | ||
2594 | while (nextSplit_Rangecc(range_Command(cmd, "packs"), ",", &path)) { | ||
2595 | if (isFirst) { | ||
2596 | appendCStr_String(src, "${glyphfinder.results}\n\n"); | ||
2597 | } | ||
2598 | iRangecc fpath = path; | ||
2599 | iRangecc fsize = path; | ||
2600 | fpath.end = strchr(fpath.start, ';'); | ||
2601 | fsize.start = fpath.end + 1; | ||
2602 | const uint32_t size = strtoul(fsize.start, NULL, 10); | ||
2603 | appendFormat_String(src, "=> gemini://skyjake.fi/fonts/%s %s (%.1f MB)\n", | ||
2604 | cstr_Rangecc(fpath), | ||
2605 | cstr_Rangecc(fpath), | ||
2606 | (double) size / 1.0e6); | ||
2607 | isFirst = iFalse; | ||
2608 | } | ||
2609 | if (isFirst) { | ||
2610 | appendFormat_String(src, "${glyphfinder.results.empty}\n"); | ||
2611 | } | ||
2612 | appendCStr_String(src, "\n=> about:fonts ${menu.fonts}"); | ||
2613 | iDocumentWidget *page = newTab_App(NULL, iTrue); | ||
2614 | translate_Lang(src); | ||
2615 | setUrlAndSource_DocumentWidget(page, | ||
2616 | collectNewCStr_String(""), | ||
2617 | collectNewCStr_String("text/gemini"), | ||
2618 | utf8_String(src)); | ||
2401 | return iTrue; | 2619 | return iTrue; |
2402 | } | 2620 | } |
2403 | #endif | ||
2404 | else if (equal_Command(cmd, "font.set")) { | 2621 | else if (equal_Command(cmd, "font.set")) { |
2405 | if (!isFrozen) { | 2622 | if (!isFrozen) { |
2406 | setFreezeDraw_MainWindow(get_MainWindow(), iTrue); | 2623 | setFreezeDraw_MainWindow(get_MainWindow(), iTrue); |
@@ -2690,6 +2907,10 @@ iBool handleCommand_App(const char *cmd) { | |||
2690 | postRefresh_App(); | 2907 | postRefresh_App(); |
2691 | return iTrue; | 2908 | return iTrue; |
2692 | } | 2909 | } |
2910 | else if (equal_Command(cmd, "prefs.dataurl.openimages.changed")) { | ||
2911 | d->prefs.openDataUrlImagesOnLoad = arg_Command(cmd) != 0; | ||
2912 | return iTrue; | ||
2913 | } | ||
2693 | else if (equal_Command(cmd, "prefs.archive.openindex.changed")) { | 2914 | else if (equal_Command(cmd, "prefs.archive.openindex.changed")) { |
2694 | d->prefs.openArchiveIndexPages = arg_Command(cmd) != 0; | 2915 | d->prefs.openArchiveIndexPages = arg_Command(cmd) != 0; |
2695 | return iTrue; | 2916 | return iTrue; |
@@ -2735,6 +2956,13 @@ iBool handleCommand_App(const char *cmd) { | |||
2735 | } | 2956 | } |
2736 | return iTrue; | 2957 | return iTrue; |
2737 | } | 2958 | } |
2959 | else if (equal_Command(cmd, "urlsize.set")) { | ||
2960 | d->prefs.maxUrlSize = arg_Command(cmd); | ||
2961 | if (d->prefs.maxUrlSize < 1024) { | ||
2962 | d->prefs.maxUrlSize = 1024; /* Gemini protocol requirement */ | ||
2963 | } | ||
2964 | return iTrue; | ||
2965 | } | ||
2738 | else if (equal_Command(cmd, "searchurl")) { | 2966 | else if (equal_Command(cmd, "searchurl")) { |
2739 | iString *url = &d->prefs.strings[searchUrl_PrefsString]; | 2967 | iString *url = &d->prefs.strings[searchUrl_PrefsString]; |
2740 | setCStr_String(url, suffixPtr_Command(cmd, "address")); | 2968 | setCStr_String(url, suffixPtr_Command(cmd, "address")); |
@@ -2817,7 +3045,18 @@ iBool handleCommand_App(const char *cmd) { | |||
2817 | return iTrue; /* invalid command */ | 3045 | return iTrue; /* invalid command */ |
2818 | } | 3046 | } |
2819 | if (findWidget_App("prefs")) { | 3047 | if (findWidget_App("prefs")) { |
2820 | postCommand_App("prefs.dismiss"); | 3048 | postCommand_App("prefs.dismiss"); |
3049 | } | ||
3050 | if (argLabel_Command(cmd, "newwindow")) { | ||
3051 | const iRangecc gotoheading = range_Command(cmd, "gotoheading"); | ||
3052 | const iRangecc gotourlheading = range_Command(cmd, "gotourlheading"); | ||
3053 | postCommandf_Root(get_Root(), "window.new%s%s%s%s url:%s", | ||
3054 | isEmpty_Range(&gotoheading) ? "" : " gotoheading:", | ||
3055 | isEmpty_Range(&gotoheading) ? "" : cstr_Rangecc(gotoheading), | ||
3056 | isEmpty_Range(&gotourlheading) ? "" : " gotourlheading:", | ||
3057 | isEmpty_Range(&gotourlheading) ? "" : cstr_Rangecc(gotourlheading), | ||
3058 | urlArg); | ||
3059 | return iTrue; | ||
2821 | } | 3060 | } |
2822 | iString *url = collectNewCStr_String(urlArg); | 3061 | iString *url = collectNewCStr_String(urlArg); |
2823 | const iBool noProxy = argLabel_Command(cmd, "noproxy") != 0; | 3062 | const iBool noProxy = argLabel_Command(cmd, "noproxy") != 0; |
@@ -2961,6 +3200,20 @@ iBool handleCommand_App(const char *cmd) { | |||
2961 | #endif | 3200 | #endif |
2962 | return iFalse; | 3201 | return iFalse; |
2963 | } | 3202 | } |
3203 | else if (equal_Command(cmd, "window.new")) { | ||
3204 | iMainWindow *newWin = new_MainWindow(initialWindowRect_App_(d, numWindows_App())); | ||
3205 | addWindow_App(newWin); /* takes ownership */ | ||
3206 | SDL_ShowWindow(newWin->base.win); | ||
3207 | setCurrent_Window(newWin); | ||
3208 | if (hasLabel_Command(cmd, "url")) { | ||
3209 | postCommandf_Root(newWin->base.roots[0], "~open %s", cmd + 11 /* all arguments passed on */); | ||
3210 | } | ||
3211 | else { | ||
3212 | postCommand_Root(newWin->base.roots[0], "~navigate.home"); | ||
3213 | } | ||
3214 | postCommand_Root(newWin->base.roots[0], "~window.unfreeze"); | ||
3215 | return iTrue; | ||
3216 | } | ||
2964 | else if (equal_Command(cmd, "tabs.new")) { | 3217 | else if (equal_Command(cmd, "tabs.new")) { |
2965 | const iBool isDuplicate = argLabel_Command(cmd, "duplicate") != 0; | 3218 | const iBool isDuplicate = argLabel_Command(cmd, "duplicate") != 0; |
2966 | newTab_App(isDuplicate ? document_App() : NULL, iTrue); | 3219 | newTab_App(isDuplicate ? document_App() : NULL, iTrue); |
@@ -3021,6 +3274,9 @@ iBool handleCommand_App(const char *cmd) { | |||
3021 | } | 3274 | } |
3022 | } | 3275 | } |
3023 | } | 3276 | } |
3277 | else if (numWindows_App() > 1) { | ||
3278 | closeWindow_App(d->window); | ||
3279 | } | ||
3024 | else { | 3280 | else { |
3025 | postCommand_App("quit"); | 3281 | postCommand_App("quit"); |
3026 | } | 3282 | } |
@@ -3050,6 +3306,7 @@ iBool handleCommand_App(const char *cmd) { | |||
3050 | setToggle_Widget(findChild_Widget(dlg, "prefs.hidetoolbarscroll"), d->prefs.hideToolbarOnScroll); | 3306 | setToggle_Widget(findChild_Widget(dlg, "prefs.hidetoolbarscroll"), d->prefs.hideToolbarOnScroll); |
3051 | setToggle_Widget(findChild_Widget(dlg, "prefs.bookmarks.addbottom"), d->prefs.addBookmarksToBottom); | 3307 | setToggle_Widget(findChild_Widget(dlg, "prefs.bookmarks.addbottom"), d->prefs.addBookmarksToBottom); |
3052 | setToggle_Widget(findChild_Widget(dlg, "prefs.font.warnmissing"), d->prefs.warnAboutMissingGlyphs); | 3308 | setToggle_Widget(findChild_Widget(dlg, "prefs.font.warnmissing"), d->prefs.warnAboutMissingGlyphs); |
3309 | setToggle_Widget(findChild_Widget(dlg, "prefs.dataurl.openimages"), d->prefs.openDataUrlImagesOnLoad); | ||
3053 | setToggle_Widget(findChild_Widget(dlg, "prefs.archive.openindex"), d->prefs.openArchiveIndexPages); | 3310 | setToggle_Widget(findChild_Widget(dlg, "prefs.archive.openindex"), d->prefs.openArchiveIndexPages); |
3054 | setToggle_Widget(findChild_Widget(dlg, "prefs.ostheme"), d->prefs.useSystemTheme); | 3311 | setToggle_Widget(findChild_Widget(dlg, "prefs.ostheme"), d->prefs.useSystemTheme); |
3055 | setToggle_Widget(findChild_Widget(dlg, "prefs.customframe"), d->prefs.customFrame); | 3312 | setToggle_Widget(findChild_Widget(dlg, "prefs.customframe"), d->prefs.customFrame); |
@@ -3122,6 +3379,8 @@ iBool handleCommand_App(const char *cmd) { | |||
3122 | collectNewFormat_String("%d", d->prefs.maxCacheSize)); | 3379 | collectNewFormat_String("%d", d->prefs.maxCacheSize)); |
3123 | setText_InputWidget(findChild_Widget(dlg, "prefs.memorysize"), | 3380 | setText_InputWidget(findChild_Widget(dlg, "prefs.memorysize"), |
3124 | collectNewFormat_String("%d", d->prefs.maxMemorySize)); | 3381 | collectNewFormat_String("%d", d->prefs.maxMemorySize)); |
3382 | setText_InputWidget(findChild_Widget(dlg, "prefs.urlsize"), | ||
3383 | collectNewFormat_String("%d", d->prefs.maxUrlSize)); | ||
3125 | setToggle_Widget(findChild_Widget(dlg, "prefs.decodeurls"), d->prefs.decodeUserVisibleURLs); | 3384 | setToggle_Widget(findChild_Widget(dlg, "prefs.decodeurls"), d->prefs.decodeUserVisibleURLs); |
3126 | setText_InputWidget(findChild_Widget(dlg, "prefs.searchurl"), &d->prefs.strings[searchUrl_PrefsString]); | 3385 | setText_InputWidget(findChild_Widget(dlg, "prefs.searchurl"), &d->prefs.strings[searchUrl_PrefsString]); |
3127 | setText_InputWidget(findChild_Widget(dlg, "prefs.ca.file"), &d->prefs.strings[caFile_PrefsString]); | 3386 | setText_InputWidget(findChild_Widget(dlg, "prefs.ca.file"), &d->prefs.strings[caFile_PrefsString]); |
@@ -3509,21 +3768,7 @@ void revealPath_App(const iString *path) { | |||
3509 | } | 3768 | } |
3510 | 3769 | ||
3511 | iObjectList *listDocuments_App(const iRoot *rootOrNull) { | 3770 | iObjectList *listDocuments_App(const iRoot *rootOrNull) { |
3512 | iWindow *win = get_Window(); | 3771 | return listDocuments_MainWindow(get_MainWindow(), rootOrNull); |
3513 | iObjectList *docs = new_ObjectList(); | ||
3514 | iForIndices(i, win->roots) { | ||
3515 | iRoot *root = win->roots[i]; | ||
3516 | if (!root) continue; | ||
3517 | if (!rootOrNull || root == rootOrNull) { | ||
3518 | const iWidget *tabs = findChild_Widget(root->widget, "doctabs"); | ||
3519 | iForEach(ObjectList, i, children_Widget(findChild_Widget(tabs, "tabs.pages"))) { | ||
3520 | if (isInstance_Object(i.object, &Class_DocumentWidget)) { | ||
3521 | pushBack_ObjectList(docs, i.object); | ||
3522 | } | ||
3523 | } | ||
3524 | } | ||
3525 | } | ||
3526 | return docs; | ||
3527 | } | 3772 | } |
3528 | 3773 | ||
3529 | iStringSet *listOpenURLs_App(void) { | 3774 | iStringSet *listOpenURLs_App(void) { |
@@ -3540,12 +3785,16 @@ iMainWindow *mainWindow_App(void) { | |||
3540 | return app_.window; | 3785 | return app_.window; |
3541 | } | 3786 | } |
3542 | 3787 | ||
3543 | void closePopups_App(void) { | 3788 | void closePopups_App(iBool doForce) { |
3544 | iApp *d = &app_; | 3789 | iApp *d = &app_; |
3545 | const uint32_t now = SDL_GetTicks(); | 3790 | const uint32_t now = SDL_GetTicks(); |
3546 | iConstForEach(PtrArray, i, &d->popupWindows) { | 3791 | iForEach(PtrArray, i, &d->popupWindows) { |
3547 | const iWindow *win = i.ptr; | 3792 | iWindow *win = i.ptr; |
3548 | if (now - win->focusGainedAt > 200) { | 3793 | // if (doForce) { |
3794 | // collect_Garbage(win, (iDeleteFunc) delete_Window); | ||
3795 | // } | ||
3796 | // else | ||
3797 | if (now - win->focusGainedAt > 200) { | ||
3549 | postCommand_Root(((const iWindow *) i.ptr)->roots[0], "cancel"); | 3798 | postCommand_Root(((const iWindow *) i.ptr)->roots[0], "cancel"); |
3550 | } | 3799 | } |
3551 | } | 3800 | } |