308 auto stuff = getMsxStuff();
312 msxFileCache.clear();
314 if (hostNeedRefresh) {
318 bool renameMsxEntry =
false;
319 bool createMsxDir =
false;
320 bool createHostDir =
false;
321 bool createDiskImage =
false;
322 bool openCheckTransfer =
false;
324 const auto& style = ImGui::GetStyle();
325 ImGui::SetNextWindowSize(
gl::vec2{70, 45} * ImGui::GetFontSize(), ImGuiCond_FirstUseEver);
327 auto availableSize = ImGui::GetContentRegionAvail();
329 auto bWidth = 2.0f * (style.ItemSpacing.x + style.FramePadding.x) + tSize.x;
330 auto cWidth = (availableSize.x - bWidth) * 0.5f;
332 bool writable = stuff && !stuff->disk->isWriteProtected();
334 int tableFlags = ImGuiTableFlags_RowBg |
335 ImGuiTableFlags_BordersV |
336 ImGuiTableFlags_BordersOuter |
337 ImGuiTableFlags_ScrollY |
338 ImGuiTableFlags_Resizable |
339 ImGuiTableFlags_Reorderable |
340 ImGuiTableFlags_Hideable |
341 ImGuiTableFlags_Sortable |
342 ImGuiTableFlags_ContextMenuInBody |
343 ImGuiTableFlags_ScrollX;
345 auto b2Width = 2.0f * style.ItemSpacing.x + 4.0f * style.FramePadding.x +
347 ImGui::SetNextItemWidth(-b2Width);
348 bool driveOk =
false;
349 im::Combo(
"##drive", driveDisplayName(selectedDrive).c_str(), [&]{
350 const auto& diskManipulator = manager.getReactor().getDiskManipulator();
351 for (
auto drives = diskManipulator.getDriveNamesForCurrentMachine();
352 const auto& drive : drives) {
353 if (selectedDrive == drive) driveOk =
true;
354 auto display = driveDisplayName(drive);
355 if (display.empty())
continue;
356 if (ImGui::Selectable(display.c_str())) {
357 selectedDrive = drive;
362 selectedDrive =
"virtual_drive";
367 createDiskImage = ImGui::Button(ICON_IGFD_ADD
"##NewDiskImage");
371 if (ImGui::Button(ICON_IGFD_FOLDER_OPEN
"##BrowseDiskImage")) insertMsxDisk();
372 simpleToolTip(
"Insert disk image");
375 if (ImGui::Button(ICON_IGFD_CHEVRON_UP
"##msxDirUp")) msxParentDirectory();
379 createMsxDir = ImGui::Button(ICON_IGFD_ADD
"##msxNewDir");
383 ImGui::SetNextItemWidth(-FLT_MIN);
386 if (ImGui::InputText(
"##msxPath", &editMsxDir)) {
387 if (!editMsxDir.starts_with(
'/')) {
388 editMsxDir =
'/' + editMsxDir;
390 if (isValidMsxDirectory(*stuff, editMsxDir)) {
398 std::string noDisk =
"No (valid) disk inserted";
399 ImGui::InputText(
"##msxPath", &noDisk);
403 im::Table(
"##msxFiles", 4, tableFlags, {-FLT_MIN, -ImGui::GetFrameHeightWithSpacing()}, [&]{
404 ImGui::TableSetupScrollFreeze(0, 1);
405 ImGui::TableSetupColumn(
"Filename", ImGuiTableColumnFlags_NoHide | ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_NoReorder);
406 ImGui::TableSetupColumn(
"Size");
407 ImGui::TableSetupColumn(
"Modified");
408 ImGui::TableSetupColumn(
"Attrib", ImGuiTableColumnFlags_DefaultHide);
409 ImGui::TableHeadersRow();
410 bool msxForceSort =
true;
411 auto action = drawTable(msxFileCache, msxLastClick, msxForceSort,
true);
415 msxDir = FileOperations::join(msxDir, cd.name);
419 stuff->tar->deleteItem(d.name);
424 renameMsxEntry =
true;
430 if (ImGui::Button(
"Export to new disk image")) exportDiskImage();
431 simpleToolTip(
"Save the content of the drive to a new disk image");
435 auto [num, size] = stuff->tar->getFreeSpace();
436 auto free = (size_t(num) * size) / 1024;
437 auto status =
strCat(free,
"kB free");
438 if (!writable)
strAppend(status,
", read-only");
446 auto pos1 = ImGui::GetCursorPos();
447 auto b2Height = ImGui::GetFrameHeightWithSpacing() + ImGui::GetFrameHeight();
448 auto byPos = (availableSize.y - b2Height) * 0.5f;
450 ImGui::Dummy({0.0f, byPos});
452 if (ImGui::Button(
"<<")) {
453 if (setupTransferHostToMsx(*stuff)) {
454 openCheckTransfer =
true;
457 simpleToolTip(
"Transfer files or directories from host to MSX");
460 if (ImGui::Button(
">>")) transferMsxToHost(*stuff);
461 simpleToolTip(
"Transfer files or directories from MSX to host");
465 ImGui::SetCursorPosY(pos1.y);
468 if (ImGui::Button(ICON_IGFD_CHEVRON_UP
"##hostDirUp")) hostParentDirectory();
471 if (ImGui::Button(ICON_IGFD_REFRESH
"##hostRefresh")) hostRefresh();
474 createHostDir = ImGui::Button(ICON_IGFD_ADD
"##hostNewDir");
477 ImGui::SetNextItemWidth(-FLT_MIN);
479 if (ImGui::InputText(
"##hostPath", &editHostDir)) {
480 if (FileOperations::isDirectory(editHostDir)) {
481 hostDir = editHostDir;
487 im::Table(
"##hostFiles", 3, tableFlags, {-FLT_MIN, -FLT_MIN}, [&]{
488 ImGui::TableSetupScrollFreeze(0, 1);
489 ImGui::TableSetupColumn(
"Filename", ImGuiTableColumnFlags_NoHide | ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_NoReorder);
490 ImGui::TableSetupColumn(
"Size");
491 ImGui::TableSetupColumn(
"Modified");
492 ImGui::TableHeadersRow();
493 auto action = drawTable(hostFileCache, hostLastClick, hostForceSort,
false);
497 hostDir = FileOperations::join(hostDir, cd.name);
500 [](Delete) { assert(
false); },
501 [](Rename) { assert(
false); }
506 const char*
const renameTitle =
"Rename";
507 if (renameMsxEntry) {
509 ImGui::OpenPopup(renameTitle);
511 im::PopupModal(renameTitle,
nullptr, ImGuiWindowFlags_AlwaysAutoResize, [&]{
518 bool ok = ImGui::InputText(
"##newName", &editModal, ImGuiInputTextFlags_EnterReturnsTrue);
519 ok |= ImGui::Button(
"Ok");
524 stuff->tar->chdir(msxDir);
525 error = stuff->tar->renameItem(renameFrom, editModal);
527 error = e.getMessage();
529 if (!error.empty()) {
530 manager.printError(
"Couldn't rename ", renameFrom,
531 " to ", editModal,
": ", error);
538 close |= ImGui::Button(
"Cancel");
539 if (close) ImGui::CloseCurrentPopup();
542 const char*
const overwriteTitle =
"Confirm overwrite?";
543 if (openCheckTransfer) {
544 ImGui::OpenPopup(overwriteTitle);
547 im::PopupModal(overwriteTitle, &p_open, ImGuiWindowFlags_AlwaysAutoResize, [&]{
548 auto printList = [](
const char* label,
const std::vector<FileInfo>& list) {
549 float count = std::min(4.5f, narrow_cast<float>(list.size()));
550 float height = count * ImGui::GetTextLineHeightWithSpacing();
552 for (
const auto& f : list) {
557 if (!existingFiles.empty()) {
559 printList(
"##files", existingFiles);
561 if (!existingDirs.empty()) {
563 printList(
"##dirs", existingDirs);
565 if (!duplicateEntries.empty()) {
567 im::ListBox(
"##duplicates", {0.0f, 4.5f * ImGui::GetTextLineHeightWithSpacing()}, [&]{
568 for (
const auto& [msxName, hostEntries] : duplicateEntries) {
569 ImGui::Text(
"%s:", msxName.c_str());
570 for (
const auto& host : hostEntries) {
571 ImGui::Text(
" %s", host.filename.c_str());
579 transferHostToMsxPhase = IDLE;
581 if (ImGui::Button(
"Preserve")) {
582 transferHostToMsxPhase = EXECUTE_PRESERVE;
587 if (ImGui::Button(
"Overwrite")) {
588 transferHostToMsxPhase = EXECUTE_OVERWRITE;
593 if (ImGui::Button(
"Cancel")) {
594 transferHostToMsxPhase = IDLE;
600 ImGui::CloseCurrentPopup();
601 if (stuff && transferHostToMsxPhase != IDLE) {
602 executeTransferHostToMsx(*stuff);
607 const char*
const newMsxDirTitle =
"Create new MSX directory";
610 ImGui::OpenPopup(newMsxDirTitle);
613 im::PopupModal(newMsxDirTitle, &p_open, ImGuiWindowFlags_AlwaysAutoResize, [&]{
618 bool ok = ImGui::InputText(
"##msxPath", &editModal, ImGuiInputTextFlags_EnterReturnsTrue);
619 ok |= ImGui::Button(
"Ok");
623 stuff->tar->chdir(msxDir);
624 stuff->tar->mkdir(editModal);
627 "Couldn't create new MSX directory: ", e.getMessage());
635 close |= ImGui::Button(
"Cancel");
636 if (close) ImGui::CloseCurrentPopup();
639 const char*
const newHostDirTitle =
"Create new host directory";
642 ImGui::OpenPopup(newHostDirTitle);
645 im::PopupModal(newHostDirTitle, &p_open, ImGuiWindowFlags_AlwaysAutoResize, [&]{
649 bool ok = ImGui::InputText(
"##hostPath", &editModal, ImGuiInputTextFlags_EnterReturnsTrue);
650 ok |= ImGui::Button(
"Ok");
652 FileOperations::mkdirp(FileOperations::join(hostDir, editModal));
657 close |= ImGui::Button(
"Cancel");
658 if (close) ImGui::CloseCurrentPopup();
661 const char*
const newDiskImageTitle =
"Create new disk image";
662 if (createDiskImage) {
663 auto current = getDiskImageName();
664 auto cwd = current.empty() ? FileOperations::getCurrentWorkingDirectory()
665 : std::string(FileOperations::getDirName(current));
666 auto newName = FileOperations::getNextNumberedFileName(cwd,
"new-image",
".dsk");
667 editModal = FileOperations::join(cwd, newName);
669 newDiskType = UNPARTITIONED;
670 bootType =
static_cast<int>(MSXBootSectorType::DOS2);
671 unpartitionedSize = {720, PartitionSize::KB};
672 partitionSizes.assign(3, {32, PartitionSize::MB});
673 ImGui::OpenPopup(newDiskImageTitle);
675 ImGui::SetNextWindowSize(
gl::vec2{30, 22} * ImGui::GetFontSize(), ImGuiCond_FirstUseEver);
680 auto buttonWidth = style.ItemSpacing.x + 2.0f * style.FramePadding.x +
682 ImGui::SetNextItemWidth(-buttonWidth);
683 ImGui::InputText(
"##newDiskPath", &editModal);
686 if (ImGui::Button(ICON_IGFD_FOLDER_OPEN
"##BrowseNewImage")) {
687 manager.openFile->selectNewFile(
688 "Filename for new disk image",
689 "Disk image (*.dsk){.dsk}",
690 [&](
const auto& fn) { editModal = fn; },
692 ImGuiOpenFile::Painter::DISKMANIPULATOR);
695 if (manager.openFile->mustPaint(ImGuiOpenFile::Painter::DISKMANIPULATOR)) {
696 manager.openFile->doPaint();
699 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 6.0f);
700 ImGui::Combo(
"type", &bootType,
"DOS 1\0DOS 2\0Nextor\0", 3);
703 ImGui::RadioButton(
"No partitions (floppy disk)", &newDiskType, UNPARTITIONED);
705 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 5.0f);
706 ImGui::InputScalar(
"##count", ImGuiDataType_U32, &unpartitionedSize.count);
708 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 3.5f);
709 ImGui::Combo(
"##unit", &unpartitionedSize.unit,
"kB\0MB\0", 2);
712 ImGui::RadioButton(
"Partitioned HD image", &newDiskType, PARTITIONED);
714 float bottomHeight = 2.0f * style.ItemSpacing.y + 1.0f + 2.0f * style.FramePadding.y + ImGui::GetTextLineHeight();
715 im::ListBox(
"##partitions", {14 * ImGui::GetFontSize(), -bottomHeight}, [&]{
717 ImGui::AlignTextToFramePadding();
718 ImGui::Text(
"%2d:", i + 1);
720 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 5.0f);
721 ImGui::InputScalar(
"##count", ImGuiDataType_U32, &partitionSizes[i].count);
723 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 3.5f);
724 ImGui::Combo(
"##unit", &partitionSizes[i].unit,
"kB\0MB\0", 2);
730 if (ImGui::Button(
"Add")) {
731 partitionSizes.push_back({32, PartitionSize::MB});
735 if (ImGui::Button(
"Remove")) {
736 partitionSizes.pop_back();
744 if (ImGui::Button(
"Ok")) {
745 const auto& diskManipulator = manager.getReactor().getDiskManipulator();
747 if (newDiskType == UNPARTITIONED) {
748 return std::vector<unsigned>(1, unpartitionedSize.asSectorCount());
750 return to_vector(view::transform(partitionSizes, &PartitionSize::asSectorCount));
754 diskManipulator.create(editModal, static_cast<MSXBootSectorType>(bootType), sizes);
755 if (auto* drive = getDrive()) {
756 drive->insertDisk(editModal);
759 } catch (MSXException& e) {
761 "Couldn't create disk image: ", e.getMessage());
767 close |= ImGui::Button(
"Cancel");
768 if (close) ImGui::CloseCurrentPopup();