/*
 * ct_dialogs_gen_purp.cc
 *
 * Copyright 2009-2026
 * Giuseppe Penone <giuspen@gmail.com>
 * Evgenii Gurianov <https://github.com/txe>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 */

#include "ct_dialogs.h"
#include "ct_main_win.h"

CtDialogTextEntry::CtDialogTextEntry(const Glib::ustring& title,
                                     const bool forPassword,
                                     Gtk::Window* pParentWin)
{
    set_title(title);
    set_transient_for(*pParentWin);
    property_destroy_with_parent() = true;
    set_modal();

    add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK)->set_always_show_image(true);
    add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL)->set_always_show_image(true);
    set_default_response(Gtk::RESPONSE_OK);

    _entry.set_icon_from_stock(Gtk::Stock::CLEAR, Gtk::ENTRY_ICON_SECONDARY);
    _entry.set_size_request(350, -1);
    if (forPassword) {
        _entry.set_visibility(false);
    }
    get_vbox()->pack_start(_entry, true, true, 0);

    // Special signals which correspond to the underlying X-Windows events are suffixed by _event.
    // By default, your signal handlers are called after any previously-connected signal handlers. However, this can be a problem with the X Event signals. For instance, the existing signal handlers, or the default signal handler, might return true to stop other signal handlers from being called. To specify that your signal handler should be called before the other signal handlers, so that it will always be called, you can specify false for the optional after parameter
    _entry.signal_key_press_event().connect(sigc::mem_fun(*this, &CtDialogTextEntry::_on_entry_key_press_event), false/*call me before other*/);

    _entry.signal_icon_press().connect(sigc::mem_fun(*this, &CtDialogTextEntry::_on_entry_icon_press));

    get_vbox()->show_all();
}

bool CtDialogTextEntry::_on_entry_key_press_event(GdkEventKey* pEventKey)
{
    if (GDK_KEY_Return == pEventKey->keyval or GDK_KEY_KP_Enter == pEventKey->keyval) {
        Gtk::Button* pButton = static_cast<Gtk::Button*>(get_widget_for_response(Gtk::RESPONSE_OK));
        pButton->grab_focus();
        pButton->clicked();
        return true;
    }
    if (GDK_KEY_Escape == pEventKey->keyval) {
        Gtk::Button* pButton = static_cast<Gtk::Button*>(get_widget_for_response(Gtk::RESPONSE_CANCEL));
        pButton->grab_focus();
        pButton->clicked();
        return true;
    }
    return false;
}

Gtk::TreeModel::iterator CtDialogs::choose_item_dialog(Gtk::Window& parent,
                                            const Glib::ustring& title,
                                            Glib::RefPtr<CtChooseDialogListStore> pModel,
                                            const gchar* single_column_name/*= nullptr*/,
                                            const std::string& pathToSelect/*= "0"*/,
                                            std::optional<std::pair<int,int>> use_size/*= std::nullopt*/,
                                            const bool column_is_colour/*= false*/)
{
    Gtk::Dialog dialog{title,
                       parent,
                       Gtk::DialogFlags::DIALOG_MODAL | Gtk::DialogFlags::DIALOG_DESTROY_WITH_PARENT};
    dialog.set_transient_for(parent);
    dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_REJECT)->set_always_show_image(true);
    dialog.add_button(Gtk::Stock::OK, Gtk::RESPONSE_ACCEPT)->set_always_show_image(true);
    dialog.set_default_response(Gtk::RESPONSE_ACCEPT);
    dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ON_PARENT);
    int use_width, use_height;
    if (use_size.has_value()) {
        use_width = use_size.value().first;
        use_height = use_size.value().second;
    }
    else {
        parent.get_size(use_width, use_height);
        use_width = 200;
    }
    dialog.set_default_size(use_width, use_height);
    auto pScrolledwindow = Gtk::manage(new Gtk::ScrolledWindow{});
    pScrolledwindow->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
    auto pElementsTreeview = Gtk::manage(new Gtk::TreeView{pModel});
    pElementsTreeview->set_headers_visible(false);
    const auto treePathToSelect = Gtk::TreePath{pathToSelect};
    pElementsTreeview->get_selection()->select(treePathToSelect);
    pElementsTreeview->signal_row_activated().connect([&](const Gtk::TreeModel::Path&, Gtk::TreeViewColumn*) {
        if (Gtk::TreeModel::iterator iter = pElementsTreeview->get_selection()->get_selected()) {
            static_cast<Gtk::Button*>(dialog.get_widget_for_response(Gtk::RESPONSE_ACCEPT))->clicked();
        }
    });

    Gtk::CellRendererPixbuf pixbuf_renderer;
    if (nullptr == single_column_name) {
        pixbuf_renderer.property_stock_size() = Gtk::BuiltinIconSize::ICON_SIZE_LARGE_TOOLBAR;
        const int col_num = pElementsTreeview->append_column("", pixbuf_renderer) - 1;
        pElementsTreeview->get_column(col_num)->add_attribute(pixbuf_renderer, "icon-name", pModel->columns.stock_id);
        pElementsTreeview->append_column("", pModel->columns.desc);
        pElementsTreeview->set_search_column(2);
    }
    else {
        const int col_num = pElementsTreeview->append_column(single_column_name, pModel->columns.desc) - 1;
        if (column_is_colour) {
            Gtk::TreeViewColumn* pTVCol = pElementsTreeview->get_column(col_num);
            std::vector<Gtk::CellRenderer*> cellRenderers = pTVCol->get_cells();
            if (cellRenderers.size() > 0) {
                auto pCellRendererText = dynamic_cast<Gtk::CellRendererText*>(cellRenderers.front());
                if (pCellRendererText) {
                    pTVCol->add_attribute(pCellRendererText->property_foreground(), pModel->columns.desc);
                }
            }
        }
    }
    pScrolledwindow->add(*pElementsTreeview);
    Gtk::Box* pContentArea = dialog.get_content_area();
    pContentArea->pack_start(*pScrolledwindow);
    pContentArea->show_all();
    pElementsTreeview->grab_focus();
    pElementsTreeview->scroll_to_row(treePathToSelect, 0.5);

    return Gtk::RESPONSE_ACCEPT == dialog.run() ? pElementsTreeview->get_selection()->get_selected() : Gtk::TreeModel::iterator{};
}

Glib::ustring CtDialogs::img_n_entry_dialog(Gtk::Window& parent,
                                            const Glib::ustring& title,
                                            const Glib::ustring& entry_content,
                                            const char* img_stock)
{
    Gtk::Dialog dialog{title,
                       parent,
                       Gtk::DialogFlags::DIALOG_MODAL | Gtk::DialogFlags::DIALOG_DESTROY_WITH_PARENT};
    dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_REJECT)->set_always_show_image(true);
    dialog.add_button(Gtk::Stock::OK, Gtk::RESPONSE_ACCEPT)->set_always_show_image(true);
    dialog.set_default_response(Gtk::RESPONSE_ACCEPT);
    dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ON_PARENT);
    dialog.set_default_size(300, -1);
    Gtk::Image image;
    image.set_from_icon_name(img_stock, Gtk::ICON_SIZE_BUTTON);
    Gtk::Entry entry;
    entry.set_text(entry_content);
    Gtk::Box hbox{Gtk::ORIENTATION_HORIZONTAL, 5/*spacing*/};
    hbox.pack_start(image, false, false);
    hbox.pack_start(entry);
    Gtk::Box* pContentArea = dialog.get_content_area();
    pContentArea->pack_start(hbox);
    pContentArea->show_all();
    entry.grab_focus();
    entry.signal_activate().connect([&](){
        if (not entry.get_text().empty()) {
            dialog.response(Gtk::RESPONSE_ACCEPT);
        }
    });

    return Gtk::RESPONSE_ACCEPT == dialog.run() ? str::trim(entry.get_text()) : "";
}

std::time_t CtDialogs::date_select_dialog(Gtk::Window& parent,
                                          const Glib::ustring& title,
                                          const std::time_t& curr_time)
{
    Gtk::Dialog dialog{title,
                       parent,
                       Gtk::DialogFlags::DIALOG_MODAL | Gtk::DialogFlags::DIALOG_DESTROY_WITH_PARENT};
    dialog.set_transient_for(parent);
    dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_REJECT)->set_always_show_image(true);
    dialog.add_button(Gtk::Stock::OK, Gtk::RESPONSE_ACCEPT)->set_always_show_image(true);
    dialog.set_default_response(Gtk::RESPONSE_ACCEPT);
    dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ON_PARENT);

    std::tm struct_time = *std::localtime(&curr_time);

    Gtk::Box* pContentArea = dialog.get_content_area();
    Gtk::Calendar calendar;
    calendar.select_month((guint)(struct_time.tm_mon), (guint)(struct_time.tm_year + 1900)); // month 0-11
    calendar.select_day((guint)struct_time.tm_mday); // day 1-31
    Glib::RefPtr<Gtk::Adjustment> rAdj_h = Gtk::Adjustment::create(struct_time.tm_hour, 0, 23, 1);
    Gtk::SpinButton spinbutton_h{rAdj_h};
    spinbutton_h.set_value(struct_time.tm_hour);
    Glib::RefPtr<Gtk::Adjustment> rAdj_m = Gtk::Adjustment::create(struct_time.tm_min, 0, 59, 1);
    Gtk::SpinButton spinbutton_m{rAdj_m};
    spinbutton_m.set_value(struct_time.tm_min);
    Gtk::Box hbox{Gtk::ORIENTATION_HORIZONTAL};
    hbox.pack_start(spinbutton_h);
    hbox.pack_start(spinbutton_m);
    pContentArea->pack_start(calendar);
    pContentArea->pack_start(hbox);

    dialog.signal_button_press_event().connect([&dialog](GdkEventButton* event)->bool{
        if ( (event->button == 1) &&
             (event->type == GDK_2BUTTON_PRESS) )
        {
            return false;
        }
        dialog.response(Gtk::RESPONSE_ACCEPT);
        return true;
    });
    pContentArea->show_all();

    if (dialog.run() != Gtk::RESPONSE_ACCEPT) {
        return 0;
    }

    guint new_year, new_month, new_day;
    calendar.get_date(new_year, new_month, new_day);

    std::tm tmtime = {};
    tmtime.tm_year = (int)new_year - 1900;
    tmtime.tm_mon = (int)(new_month);
    tmtime.tm_mday = (int)new_day;
    tmtime.tm_hour = spinbutton_h.get_value_as_int();
    tmtime.tm_min = spinbutton_m.get_value_as_int();

    std::time_t new_time = std::mktime(&tmtime);
    return new_time;
}

CtDialogs::CtPickDlgState CtDialogs::colour_pick_dialog(CtMainWin* pCtMainWin,
                                                        const Glib::ustring& title,
                                                        Glib::ustring& ret_colour,
                                                        bool allow_remove_colour)
{
    Gtk::ColorChooserDialog dialog{title};
    dialog.set_transient_for(*pCtMainWin);
    dialog.set_modal(true);
    dialog.property_destroy_with_parent() = true;
    dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ON_PARENT);
    // The ColorChooserDialog adds Cancel and Select Color buttons internally using plain text
    // (no stock image), so we must explicitly set images and force them to show
    if (auto* pBtnCancel = static_cast<Gtk::Button*>(dialog.get_widget_for_response(Gtk::RESPONSE_CANCEL))) {
        pBtnCancel->set_image(*pCtMainWin->new_managed_image_from_stock("ct_close", Gtk::ICON_SIZE_BUTTON));
        pBtnCancel->set_always_show_image(true);
    }
    if (auto* pBtnOk = static_cast<Gtk::Button*>(dialog.get_widget_for_response(Gtk::RESPONSE_OK))) {
        pBtnOk->set_image(*pCtMainWin->new_managed_image_from_stock("ct_painting", Gtk::ICON_SIZE_BUTTON));
        pBtnOk->set_always_show_image(true);
    }
    if (allow_remove_colour) {
        Gtk::Button* pBtnRemove = dialog.add_button(_("Remove"), Gtk::RESPONSE_NONE);
        pBtnRemove->set_image(*pCtMainWin->new_managed_image_from_stock("ct_remove", Gtk::ICON_SIZE_BUTTON));
        pBtnRemove->set_always_show_image(true);
    }
    // from gtk3 branch gtk-3-24 file gtk/gtk/gtkcolorchooserwidget.c function add_default_palette
    const gchar* default_colors[45]{
        "#99c1f1", "#62a0ea", "#3584e4", "#1c71d8", "#1a5fb4", /* Blue */
        "#8ff0a4", "#57e389", "#33d17a", "#2ec27e", "#26a269", /* Green */
        "#f9f06b", "#f8e45c", "#f6d32d", "#f5c211", "#e5a50a", /* Yellow */
        "#ffbe6f", "#ffa348", "#ff7800", "#e66100", "#c64600", /* Orange */
        "#f66151", "#ed333b", "#e01b24", "#c01c28", "#a51d2d", /* Red */
        "#dc8add", "#c061cb", "#9141ac", "#813d9c", "#613583", /* Purple */
        "#cdab8f", "#b5835a", "#986a44", "#865e3c", "#63452c", /* Brown */
        "#ffffff", "#f6f5f4", "#deddda", "#c0bfbc", "#9a9996", /* Light */
        "#77767b", "#5e5c64", "#3d3846", "#241f31", "#000000"};/* Dark */
    std::vector<Gdk::RGBA> default_colours;
    auto& coloursUserPalette = pCtMainWin->get_ct_config()->coloursUserPalette;
    auto f_add_palette = [&](){
        default_colours.clear();
        std::vector<Gdk::RGBA> palette_colours;
        size_t column_idx{0u};
        for (int i = 0; i < 45; ++i) {
            const Gdk::RGBA curr_colour{default_colors[i]};
            default_colours.push_back(curr_colour);
            palette_colours.push_back(curr_colour);
            if (coloursUserPalette.size() > 0u) {
                if (4 == i % 5) {
                    if (coloursUserPalette.size() > column_idx) palette_colours.push_back(*coloursUserPalette.at(column_idx));
                    else palette_colours.push_back(Gdk::RGBA{});
                    if (coloursUserPalette.size() > 9u) {
                        if (coloursUserPalette.size() > (9+column_idx)) palette_colours.push_back(*coloursUserPalette.at(9+column_idx));
                        else palette_colours.push_back(Gdk::RGBA{});
                    }
                    ++column_idx;
                }
            }
        }
        dialog.add_palette(Gtk::Orientation::ORIENTATION_VERTICAL, 5 + (coloursUserPalette.size() + 8)/9, palette_colours);
    };
    f_add_palette();
    dialog.set_rgba(Gdk::RGBA{ret_colour});

    auto on_key_press_dialog = [&](GdkEventKey* pEventKey)->bool{
        if (GDK_KEY_Return == pEventKey->keyval or GDK_KEY_KP_Enter == pEventKey->keyval) {
            Gtk::Button* pButton = static_cast<Gtk::Button*>(dialog.get_widget_for_response(Gtk::RESPONSE_OK));
            pButton->grab_focus();
            pButton->clicked();
            return true;
        }
        return false;
    };
    dialog.signal_key_press_event().connect(on_key_press_dialog, false/*call me before other*/);

    Gtk::Box* pContentArea = dialog.get_content_area();
    std::vector<Gtk::Widget*> content_children = pContentArea->get_children();
    //spdlog::debug("{} content_children", content_children.size());
    if (content_children.size() > 0u) {
        auto pInnerBox = dynamic_cast<Gtk::Box*>(content_children.front());
        if (pInnerBox) {
            std::vector<Gtk::Widget*> inner_children = pInnerBox->get_children();
            //spdlog::debug("{} inner_children", inner_children.size());
            if (inner_children.size() > 0u) {
                auto pInner2Box = dynamic_cast<Gtk::Box*>(inner_children.front());
                if (pInner2Box) {
                    std::vector<Gtk::Widget*> inner2_children = pInner2Box->get_children();
                    spdlog::debug("{} inner2_children", inner2_children.size());
                    if (3u == inner2_children.size()) {
                        inner2_children.at(1)->hide();
                        inner2_children.at(2)->hide();
                        auto hbox_more_less = Gtk::manage(new Gtk::Box{Gtk::ORIENTATION_HORIZONTAL, 4/*spacing*/});
                        auto button_more = Gtk::manage(new Gtk::Button{});
                        button_more->set_image(*pCtMainWin->new_managed_image_from_stock("ct_add", Gtk::ICON_SIZE_BUTTON));
                        hbox_more_less->pack_start(*button_more, false, false);
                        auto button_less = Gtk::manage(new Gtk::Button{});
                        button_less->set_image(*pCtMainWin->new_managed_image_from_stock("ct_remove", Gtk::ICON_SIZE_BUTTON));
                        button_less->set_sensitive(coloursUserPalette.size() > 0u);
                        hbox_more_less->pack_start(*button_less, false, false);
                        hbox_more_less->show_all();
                        pInner2Box->pack_start(*hbox_more_less, false, false);
                        button_more->signal_clicked().connect([&](){
                            dialog.property_show_editor() = true;
                        });
                        button_less->signal_clicked().connect([&](){
                            auto itemStore = CtChooseDialogListStore::create();
                            for (const Gdk::RGBA& curr_rgba : coloursUserPalette) {
                                itemStore->add_row("", "", curr_rgba.to_string());
                            }
                            const Gtk::TreeModel::iterator treeIter = CtDialogs::choose_item_dialog(dialog,
                                                                                         _("Remove User Colour from Palette"),
                                                                                         itemStore,
                                                                                         _("Colour"),
                                                                                         "0"/*pathToSelect*/,
                                                                                         std::nullopt/*use_size*/,
                                                                                         true/*column_is_colour*/);
                            if (treeIter) {
                                const Glib::ustring colour_to_rm = treeIter->get_value(itemStore->columns.desc);
                                for (const Gdk::RGBA& curr_rgba : coloursUserPalette) {
                                    if (colour_to_rm == curr_rgba.to_string()) {
                                        coloursUserPalette.remove(curr_rgba);
                                        break;
                                    }
                                }
                                dialog.response(Gtk::RESPONSE_HELP);
                            }
                        });
                    }
                }
            }
        }
    }

    const int response = dialog.run();

    if (Gtk::RESPONSE_NONE == response) {
        return CtPickDlgState::REMOVE_COLOR;
    }
    if (Gtk::RESPONSE_HELP == response) {
        return CtPickDlgState::CALL_AGAIN;
    }
    if (Gtk::RESPONSE_OK != response) {
        return CtPickDlgState::CANCEL;
    }
    const Gdk::RGBA sel_colour = dialog.get_rgba();
    ret_colour = CtRgbUtil::rgb_to_string_24(sel_colour);
    if (not vec::exists(default_colours, sel_colour)) {
        coloursUserPalette.move_or_push_front(sel_colour);
    }
    return CtPickDlgState::SELECTED;
}

// Returns True if the user presses OK
bool CtDialogs::question_dialog(const Glib::ustring& message,
                                Gtk::Window& parent)
{
    Gtk::MessageDialog dialog{parent,
                              message,
                              true/*use_markup*/,
                              Gtk::MESSAGE_QUESTION,
                              Gtk::BUTTONS_YES_NO,
                              true/*modal*/};
    dialog.set_title(_("Question"));
    dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ON_PARENT);
    dialog.property_destroy_with_parent() = true;
    auto* pBtnYes = static_cast<Gtk::Button*>(dialog.get_widget_for_response(Gtk::RESPONSE_YES));
    pBtnYes->set_image_from_icon_name("ct_done", Gtk::ICON_SIZE_BUTTON);
    pBtnYes->set_always_show_image(true);
    pBtnYes->grab_focus();
    auto* pBtnNo = static_cast<Gtk::Button*>(dialog.get_widget_for_response(Gtk::RESPONSE_NO));
    pBtnNo->set_image_from_icon_name("ct_close", Gtk::ICON_SIZE_BUTTON);
    pBtnNo->set_always_show_image(true);
    return (Gtk::RESPONSE_YES == dialog.run());
}

void CtDialogs::info_dialog(const Glib::ustring& message,
                            Gtk::Window& parent)
{
    Gtk::MessageDialog dialog{parent,
                              message,
                              true/*use_markup*/,
                              Gtk::MESSAGE_INFO,
                              Gtk::BUTTONS_OK,
                              true/*modal*/};
    dialog.set_title(_("Info"));
    dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ON_PARENT);
    dialog.property_destroy_with_parent() = true;
    auto* pBtnOkInfo = static_cast<Gtk::Button*>(dialog.get_widget_for_response(Gtk::RESPONSE_OK));
    pBtnOkInfo->set_image_from_icon_name("ct_done", Gtk::ICON_SIZE_BUTTON);
    pBtnOkInfo->set_always_show_image(true);
    dialog.run();
}

void CtDialogs::warning_dialog(const Glib::ustring& message,
                               Gtk::Window& parent)
{
    Gtk::MessageDialog dialog{parent,
                              message,
                              true/*use_markup*/,
                              Gtk::MESSAGE_WARNING,
                              Gtk::BUTTONS_OK,
                              true/*modal*/};
    dialog.set_title(_("Warning"));
    dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ON_PARENT);
    dialog.property_destroy_with_parent() = true;
    auto* pBtnOkWarn = static_cast<Gtk::Button*>(dialog.get_widget_for_response(Gtk::RESPONSE_OK));
    pBtnOkWarn->set_image_from_icon_name("ct_done", Gtk::ICON_SIZE_BUTTON);
    pBtnOkWarn->set_always_show_image(true);
    dialog.run();
}

void CtDialogs::error_dialog(const Glib::ustring& message,
                             Gtk::Window& parent)
{
    Gtk::MessageDialog dialog{parent,
                              message,
                              true/*use_markup*/,
                              Gtk::MESSAGE_ERROR,
                              Gtk::BUTTONS_OK,
                              true/*modal*/};
    dialog.set_title(_("Error"));
    dialog.set_position(Gtk::WindowPosition::WIN_POS_CENTER_ON_PARENT);
    dialog.property_destroy_with_parent() = true;
    auto* pBtnOkErr = static_cast<Gtk::Button*>(dialog.get_widget_for_response(Gtk::RESPONSE_OK));
    pBtnOkErr->set_image_from_icon_name("ct_done", Gtk::ICON_SIZE_BUTTON);
    pBtnOkErr->set_always_show_image(true);
    dialog.run();
}

std::string CtDialogs::file_select_dialog(Gtk::Window* pParentWin, const CtFileSelectArgs& args)
{
#if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION >= 24)
    auto chooser = Gtk::FileChooserNative::create(_("Select File"), *pParentWin, Gtk::FILE_CHOOSER_ACTION_OPEN);
#else
    auto chooser = std::make_unique<Gtk::FileChooserDialog>(*pParentWin, _("Select File"), Gtk::FILE_CHOOSER_ACTION_OPEN);
    chooser->add_button(Gtk::StockID{GTK_STOCK_CANCEL}, Gtk::RESPONSE_CANCEL);
    chooser->add_button(Gtk::StockID{GTK_STOCK_OPEN}, Gtk::RESPONSE_ACCEPT);
    chooser->property_destroy_with_parent() = true;
#endif
    if (args.curr_folder.empty() or not fs::is_directory(args.curr_folder)) {
        chooser->set_current_folder(Glib::get_home_dir());
    }
    else {
        chooser->set_current_folder(args.curr_folder.string());
    }
    if (not args.filter_pattern.empty() or not args.filter_mime.empty()) {
        Glib::RefPtr<Gtk::FileFilter> rFileFilter = Gtk::FileFilter::create();
        rFileFilter->set_name(args.filter_name);
        for (const auto& element : args.filter_pattern) {
            rFileFilter->add_pattern(element);
        }
        for (const auto& element : args.filter_mime) {
            rFileFilter->add_mime_type(element);
        }
        chooser->add_filter(rFileFilter);
    }
    return chooser->run() == Gtk::RESPONSE_ACCEPT ? chooser->get_filename() : "";
}

std::string CtDialogs::folder_select_dialog(Gtk::Window* pParentWin, const std::string& curr_folder)
{
#if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION >= 24)
    auto chooser = Gtk::FileChooserNative::create(_("Select Folder"), *pParentWin, Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER);
#else
    auto chooser = std::make_unique<Gtk::FileChooserDialog>(*pParentWin, _("Select Folder"), Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER);
    chooser->add_button(Gtk::StockID{GTK_STOCK_CANCEL}, Gtk::RESPONSE_CANCEL);
    chooser->add_button(Gtk::StockID{GTK_STOCK_OPEN}, Gtk::RESPONSE_ACCEPT);
    chooser->property_destroy_with_parent() = true;
#endif
    if (curr_folder.empty() or not Glib::file_test(curr_folder, Glib::FILE_TEST_IS_DIR)) {
        chooser->set_current_folder(g_get_home_dir());
    }
    else {
        chooser->set_current_folder(curr_folder);
    }
    return chooser->run() == Gtk::RESPONSE_ACCEPT ? chooser->get_filename() : "";
}

std::string CtDialogs::file_save_as_dialog(Gtk::Window* pParentWin, const CtFileSelectArgs& args)
{
#if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION >= 24)
    auto chooser = Gtk::FileChooserNative::create(_("Save File as"), *pParentWin, Gtk::FILE_CHOOSER_ACTION_SAVE);
#else
    auto chooser = std::make_unique<Gtk::FileChooserDialog>(*pParentWin, _("Save File as"), Gtk::FILE_CHOOSER_ACTION_SAVE);
    chooser->add_button(Gtk::StockID{GTK_STOCK_CANCEL}, Gtk::RESPONSE_CANCEL);
    chooser->add_button(Gtk::StockID{GTK_STOCK_SAVE_AS}, Gtk::RESPONSE_ACCEPT);
    chooser->property_destroy_with_parent() = true;
#endif
    chooser->set_do_overwrite_confirmation(args.overwrite_confirmation);
    if (args.curr_folder.empty() or not fs::is_directory(args.curr_folder)) {
        chooser->set_current_folder(g_get_home_dir());
    }
    else {
        chooser->set_current_folder(args.curr_folder.string());
    }
    if (not args.curr_file_name.empty()) {
        chooser->set_current_name(args.curr_file_name.string());
    }
    if (not args.filter_pattern.empty()) {
        Glib::RefPtr<Gtk::FileFilter> rFileFilter = Gtk::FileFilter::create();
        rFileFilter->set_name(args.filter_name);
        for (const Glib::ustring& element : args.filter_pattern) {
            rFileFilter->add_pattern(element);
        }
        chooser->add_filter(rFileFilter);
    }
    return chooser->run() == Gtk::RESPONSE_ACCEPT ? chooser->get_filename() : "";
}

std::string CtDialogs::folder_save_as_dialog(Gtk::Window* pParentWin, const CtFileSelectArgs& args)
{
//#if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION >= 24)
#if 0 // native create folder doesn't seem to work
    auto chooser = Gtk::FileChooserNative::create(_("Save To Folder"), *pParentWin, Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER);
#else
    auto chooser = std::make_unique<Gtk::FileChooserDialog>(*pParentWin, _("Save To Folder"), Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER);
    chooser->add_button(Gtk::StockID{GTK_STOCK_CANCEL}, Gtk::RESPONSE_CANCEL);
    chooser->add_button(Gtk::StockID{GTK_STOCK_SAVE_AS}, Gtk::RESPONSE_ACCEPT);
    chooser->property_destroy_with_parent() = true;
#endif
    //chooser->set_do_overwrite_confirmation(true); unfortunately works only with Gtk::FILE_CHOOSER_ACTION_SAVE
    if (args.curr_folder.empty() or not fs::is_directory(args.curr_folder)) {
        chooser->set_current_folder(g_get_home_dir());
    }
    else {
        chooser->set_current_folder(args.curr_folder.string());
    }
    if (not args.curr_file_name.empty()) {
        chooser->set_current_name(args.curr_file_name.string());
    }
    return chooser->run() == Gtk::RESPONSE_ACCEPT ? chooser->get_filename() : "";
}
