LCOV - code coverage report
Current view: top level - src/gui/dialogs - file_dialog_native_open.cpp (source / functions) Coverage Total Hit
Test: merged.info Lines: 2.1 % 97 2
Test Date: 2026-06-01 11:15:25 Functions: 50.0 % 2 1

            Line data    Source code
       1              : // =============================================================================
       2              : // Native file dialog implementations (Windows, macOS, Linux)
       3              : // Open dialog implementation
       4              : // =============================================================================
       5              : 
       6              : #include "gui/dialogs/file_dialog.h"
       7              : #include <cstring>
       8              : 
       9              : #ifdef _WIN32
      10              : #define WIN32_LEAN_AND_MEAN
      11              : #include <windows.h>
      12              : #include <commdlg.h>
      13              : #endif
      14              : 
      15              : #ifdef __APPLE__
      16              : #include <TargetConditionals.h>
      17              : #include <cstdio>
      18              : #include <unistd.h>
      19              : #include <sys/wait.h>
      20              : #include <fcntl.h>
      21              : #endif
      22              : 
      23              : #ifndef _WIN32
      24              : #include <sys/wait.h>
      25              : #endif
      26              : 
      27              : namespace Amplitron {
      28              : 
      29              : #ifdef AMPLITRON_HEADLESS
      30            3 : std::string show_open_dialog(const std::string&, const std::string&, const std::string&) {
      31            4 :     return "";
      32              : }
      33              : #else
      34              : 
      35              : #ifdef _WIN32
      36            0 : std::string show_open_dialog(const std::string& title,
      37              :                              const std::string& filter_desc,
      38              :                              const std::string& filter_ext) {
      39            0 :     char filename[MAX_PATH] = "";
      40              : 
      41            0 :     char filter[256];
      42            0 :     std::memset(filter, 0, sizeof(filter));
      43            0 :     int pos = 0;
      44            0 :     pos += snprintf(filter + pos, 256 - pos, "%s (*.%s)", filter_desc.c_str(), filter_ext.c_str());
      45            0 :     pos++;
      46            0 :     pos += snprintf(filter + pos, 256 - pos, "*.%s", filter_ext.c_str());
      47            0 :     pos++;
      48            0 :     pos += snprintf(filter + pos, 256 - pos, "All Files (*.*)");
      49            0 :     pos++;
      50            0 :     pos += snprintf(filter + pos, 256 - pos, "*.*");
      51              : 
      52            0 :     OPENFILENAMEA ofn;
      53            0 :     std::memset(&ofn, 0, sizeof(ofn));
      54            0 :     ofn.lStructSize = sizeof(ofn);
      55            0 :     ofn.hwndOwner = NULL;
      56            0 :     ofn.lpstrFilter = filter;
      57            0 :     ofn.lpstrFile = filename;
      58            0 :     ofn.nMaxFile = MAX_PATH;
      59            0 :     ofn.lpstrTitle = title.c_str();
      60            0 :     ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR;
      61              : 
      62            0 :     if (GetOpenFileNameA(&ofn)) {
      63            0 :         return std::string(filename);
      64              :     }
      65            0 :     return "";
      66              : }
      67              : 
      68              : #elif defined(__APPLE__) && !TARGET_OS_IOS
      69            0 : std::string show_open_dialog(const std::string& title,
      70              :                              const std::string& /*filter_desc*/,
      71              :                              const std::string& filter_ext) {
      72              :     // Sanitize title and filter_ext for AppleScript
      73            0 :     std::string safe_title;
      74            0 :     for (char c : title) {
      75            0 :         if (c == '\\') { safe_title += "\\\\"; }
      76            0 :         else if (c == '"') { safe_title += "\\\""; }
      77            0 :         else { safe_title += c; }
      78              :     }
      79              : 
      80            0 :     std::string safe_ext;
      81            0 :     for (char c : filter_ext) {
      82            0 :         if (c == '\\') { safe_ext += "\\\\"; }
      83            0 :         else if (c == '"') { safe_ext += "\\\""; }
      84            0 :         else { safe_ext += c; }
      85              :     }
      86              : 
      87            0 :     std::string script = "POSIX path of (choose file of type {\"" + safe_ext +
      88            0 :                          "\"} with prompt \"" + safe_title + "\")";
      89              : 
      90              :     // Use fork+exec to invoke osascript directly
      91              :     int pipefd[2];
      92            0 :     if (pipe(pipefd) != 0) return "";
      93              : 
      94            0 :     pid_t pid = fork();
      95            0 :     if (pid < 0) {
      96            0 :         close(pipefd[0]);
      97            0 :         close(pipefd[1]);
      98            0 :         return "";
      99              :     }
     100              : 
     101            0 :     if (pid == 0) {
     102            0 :         close(pipefd[0]);
     103            0 :         dup2(pipefd[1], STDOUT_FILENO);
     104            0 :         close(pipefd[1]);
     105            0 :         int devnull = open("/dev/null", O_WRONLY);
     106            0 :         if (devnull >= 0) { dup2(devnull, STDERR_FILENO); close(devnull); }
     107            0 :         execl("/usr/bin/osascript", "osascript", "-e", script.c_str(), nullptr);
     108            0 :         _exit(1);
     109              :     }
     110              : 
     111            0 :     close(pipefd[1]);
     112              :     char buf[1024];
     113            0 :     std::string result;
     114              :     ssize_t n;
     115            0 :     while ((n = read(pipefd[0], buf, sizeof(buf))) > 0)
     116            0 :         result.append(buf, static_cast<size_t>(n));
     117            0 :     close(pipefd[0]);
     118              : 
     119            0 :     int status = 0;
     120            0 :     waitpid(pid, &status, 0);
     121              : 
     122            0 :     while (!result.empty() && (result.back() == '\n' || result.back() == '\r'))
     123            0 :         result.pop_back();
     124              : 
     125            0 :     return result;
     126            0 : }
     127              : 
     128              : #else // Linux
     129            0 : std::string show_open_dialog(const std::string& title,
     130              :                              const std::string& filter_desc,
     131              :                              const std::string& filter_ext) {
     132              :     // Escape single quotes for shell
     133            0 :     auto escape_single_quotes = [](const std::string& s) {
     134            0 :         std::string result;
     135            0 :         for (char c : s) {
     136            0 :             if (c == '\'') { result += "'\\''"; }
     137            0 :             else { result += c; }
     138              :         }
     139            0 :         return result;
     140            0 :     };
     141              : 
     142            0 :     std::string safe_title = escape_single_quotes(title);
     143            0 :     std::string safe_desc = escape_single_quotes(filter_desc);
     144            0 :     std::string safe_ext = escape_single_quotes(filter_ext);
     145              : 
     146              :     std::string cmd = "zenity --file-selection "
     147            0 :                       "--title='" + safe_title + "' "
     148            0 :                       "--file-filter='" + safe_desc + " (*." + safe_ext + ")|*." + safe_ext + "' "
     149            0 :                       "--file-filter='All Files (*)|*' 2>/dev/null";
     150              : 
     151            0 :     FILE* pipe = popen(cmd.c_str(), "r");
     152            0 :     if (!pipe) return "";
     153              : 
     154              :     char buf[1024];
     155            0 :     std::string result;
     156            0 :     while (fgets(buf, sizeof(buf), pipe)) {
     157            0 :         result += buf;
     158              :     }
     159            0 :     int wait_status = pclose(pipe);
     160              : 
     161            0 :     if (WIFEXITED(wait_status) && WEXITSTATUS(wait_status) != 0) {
     162            0 :         cmd = "kdialog --getopenfilename ~/ '*." + safe_ext + "|" + safe_desc + "' "
     163            0 :               "--title '" + safe_title + "' 2>/dev/null";
     164            0 :         pipe = popen(cmd.c_str(), "r");
     165            0 :         if (!pipe) return "";
     166            0 :         result.clear();
     167            0 :         while (fgets(buf, sizeof(buf), pipe)) {
     168            0 :             result += buf;
     169              :         }
     170            0 :         pclose(pipe);
     171              :     }
     172              : 
     173            0 :     while (!result.empty() && (result.back() == '\n' || result.back() == '\r'))
     174            0 :         result.pop_back();
     175              : 
     176            0 :     return result;
     177            0 : }
     178              : #endif
     179              : 
     180              : #endif // AMPLITRON_HEADLESS
     181              : 
     182              : } // namespace Amplitron
        

Generated by: LCOV version 2.0-1