diff --git a/Makefile b/Makefile index e93d97ea95..ba73a32b8a 100644 --- a/Makefile +++ b/Makefile @@ -150,8 +150,29 @@ ifneq ($(findstring $(GPERFTOOLS),tcmalloc),) LIBS += -ltcmalloc endif +# Qt MOC generation, required for QObject-derived classes +ifneq ($(MOC_HEADERS),) + # prefix moc_ to base filename of paths and change extension from h to cpp, so a/b/foo.h becomes a/b/moc_foo.cpp + MOC_SRC := $(join $(addsuffix moc_,$(addprefix $(OBJDIR)/,$(dir $(MOC_HEADERS)))), $(notdir $(MOC_HEADERS:.h=.cpp))) + MOC_OBJ := $(patsubst %.cpp,%.o,$(MOC_SRC)) + RARCH_OBJ += $(MOC_OBJ) +endif + all: $(TARGET) config.mk +$(MOC_SRC): + @$(if $(Q), $(shell echo echo MOC $<),) + $(eval MOC_TMP := $(patsubst %.h,%_moc.cpp,$@)) + $(Q)$(MOC) -o $(MOC_TMP) $< + +$(foreach x,$(join $(addsuffix :,$(MOC_SRC)),$(MOC_HEADERS)),$(eval $x)) + +$(MOC_OBJ): + @$(if $(Q), $(shell echo echo CXX $<),) + $(Q)$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(DEFINES) -MMD -c -o $@ $< + +$(foreach x,$(join $(addsuffix :,$(MOC_OBJ)),$(MOC_SRC)),$(eval $x)) + ifeq ($(MAKECMDGOALS),clean) config.mk: else @@ -250,3 +271,6 @@ clean: rm -f *.d .PHONY: all install uninstall clean + +print-%: + @echo '$*=$($*)' diff --git a/Makefile.common b/Makefile.common index d17d8a3562..c3ac7be651 100644 --- a/Makefile.common +++ b/Makefile.common @@ -324,23 +324,27 @@ DEFINES += -DHAVE_IMAGEVIEWER OBJ += cores/libretro-imageviewer/image_core.o endif -# Qt - -ifeq ($(HAVE_QT_WRAPPER), 1) -OBJ += ui/drivers/ui_qt.o -LIBS += -lQt5Quick -lQt5Widgets -lQt5Gui -lQt5Qml -lQt5Network -lQt5Core -L./ui/drivers/qt/build/release/ -LIBS += -lwrapper -endif +# Qt WIMP GUI ifeq ($(HAVE_QT), 1) OBJ += ui/drivers/ui_qt.o \ ui/drivers/qt/ui_qt_application.o \ ui/drivers/qt/ui_qt_window.o \ ui/drivers/qt/ui_qt_browser_window.o \ + ui/drivers/qt/ui_qt_load_core_window.o \ ui/drivers/qt/ui_qt_msg_window.o +MOC_HEADERS += ui/drivers/ui_qt.h \ + ui/drivers/qt/ui_qt_load_core_window.h + +DEFINES += $(QT5CORE_CFLAGS) $(QT5GUI_CFLAGS) $(QT5WIDGETS_CFLAGS) -DHAVE_MAIN +#DEFINES += $(QT5WEBENGINE_CFLAGS) +LIBS += $(QT5CORE_LIBS) $(QT5GUI_LIBS) $(QT5WIDGETS_LIBS) +#LIBS += $(QT5WEBENGINE_LIBS) +NEED_CXX_LINKER = 1 + ifneq ($(findstring Linux,$(OS)),) -DEFINES += -I/usr/include/qt -fPIC +DEFINES += -fPIC endif endif @@ -1185,7 +1189,7 @@ ifeq ($(HAVE_VULKAN), 1) ifeq ($(HAVE_MENU_COMMON), 1) OBJ += menu/drivers_display/menu_display_vulkan.o endif - LIBS += -lstdc++ + #LIBS += -lstdc++ DEFINES += -DHAVE_VULKAN INCLUDE_DIRS += -Igfx/include diff --git a/command.c b/command.c index 1a279788d8..02223414ef 100644 --- a/command.c +++ b/command.c @@ -169,6 +169,7 @@ static const struct cmd_map map[] = { { "DISK_NEXT", RARCH_DISK_NEXT }, { "DISK_PREV", RARCH_DISK_PREV }, { "GRAB_MOUSE_TOGGLE", RARCH_GRAB_MOUSE_TOGGLE }, + { "UI_COMPANION_TOGGLE", RARCH_UI_COMPANION_TOGGLE }, { "GAME_FOCUS_TOGGLE", RARCH_GAME_FOCUS_TOGGLE }, { "MENU_TOGGLE", RARCH_MENU_TOGGLE }, { "MENU_UP", RETRO_DEVICE_ID_JOYPAD_UP }, @@ -1705,7 +1706,7 @@ void command_playlist_update_write( const char *core_path, const char *core_display_name, const char *crc32, - const char *db_name) + const char *db_name) { playlist_t *plist = (playlist_t*)data; playlist_t *playlist = plist ? plist : playlist_get_cached(); @@ -1787,10 +1788,11 @@ bool command_event(enum event_command cmd, void *data) return false; #endif - libretro_get_system_info( + if (!libretro_get_system_info( core_path, system, - &system_info->load_no_content); + &system_info->load_no_content)) + return false; info_find.path = core_path; if (!core_info_load(&info_find)) @@ -1803,11 +1805,17 @@ bool command_event(enum event_command cmd, void *data) } break; case CMD_EVENT_LOAD_CORE: - command_event(CMD_EVENT_LOAD_CORE_PERSIST, NULL); + { + bool success = command_event(CMD_EVENT_LOAD_CORE_PERSIST, NULL); + #ifndef HAVE_DYNAMIC command_event(CMD_EVENT_QUIT, NULL); +#else + if (!success) + return false; #endif break; + } case CMD_EVENT_LOAD_STATE: /* Immutable - disallow savestate load when * we absolutely cannot change game state. */ @@ -1925,7 +1933,7 @@ bool command_event(enum event_command cmd, void *data) cheevos_toggle_hardcore_mode(); #endif break; - /* this fallthrough is on purpose, it should do + /* this fallthrough is on purpose, it should do a CMD_EVENT_REINIT too */ case CMD_EVENT_REINIT_FROM_TOGGLE: retroarch_unset_forced_fullscreen(); @@ -2010,7 +2018,7 @@ TODO: Add a setting for these tweaks */ case CMD_EVENT_AUTOSAVE_INIT: command_event(CMD_EVENT_AUTOSAVE_DEINIT, NULL); #ifdef HAVE_THREADS - { + { #ifdef HAVE_NETWORKING /* Only enable state manager if netplay is not underway TODO: Add a setting for these tweaks */ @@ -2024,7 +2032,7 @@ TODO: Add a setting for these tweaks */ else runloop_unset(RUNLOOP_ACTION_AUTOSAVE); } - } + } #endif break; case CMD_EVENT_AUTOSAVE_STATE: @@ -2306,7 +2314,7 @@ TODO: Add a setting for these tweaks */ case CMD_EVENT_RESUME: rarch_menu_running_finished(); if (ui_companion_is_on_foreground()) - ui_companion_driver_toggle(); + ui_companion_driver_toggle(false); break; case CMD_EVENT_ADD_TO_FAVORITES: { @@ -2355,7 +2363,7 @@ TODO: Add a setting for these tweaks */ runloop_msg_queue_push(msg_hash_to_str(MSG_RESET_CORE_ASSOCIATION), 1, 180, true); break; - } + } case CMD_EVENT_RESTART_RETROARCH: if (!frontend_driver_set_fork(FRONTEND_FORK_RESTART)) return false; @@ -2577,7 +2585,7 @@ TODO: Add a setting for these tweaks */ case CMD_EVENT_FULLSCREEN_TOGGLE: { settings_t *settings = config_get_ptr(); - bool new_fullscreen_state = !settings->bools.video_fullscreen + bool new_fullscreen_state = !settings->bools.video_fullscreen && !retroarch_is_forced_fullscreen(); if (!video_driver_has_windowed()) return false; @@ -2736,6 +2744,11 @@ TODO: Add a setting for these tweaks */ video_driver_show_mouse(); } break; + case CMD_EVENT_UI_COMPANION_TOGGLE: + { + ui_companion_driver_toggle(true); + break; + } case CMD_EVENT_GAME_FOCUS_TOGGLE: { static bool game_focus_state = false; diff --git a/command.h b/command.h index 9cc3e3ce17..139db92890 100644 --- a/command.h +++ b/command.h @@ -139,7 +139,7 @@ enum event_command /* Add a playlist entry to favorites. */ CMD_EVENT_ADD_TO_FAVORITES, /* Reset playlist entry associated core to DETECT */ - CMD_EVENT_RESET_CORE_ASSOCIATION, + CMD_EVENT_RESET_CORE_ASSOCIATION, /* Toggles pause. */ CMD_EVENT_PAUSE_TOGGLE, /* Pauses RetroArch. */ @@ -218,6 +218,8 @@ enum event_command CMD_EVENT_GRAB_MOUSE_TOGGLE, /* Toggles game focus. */ CMD_EVENT_GAME_FOCUS_TOGGLE, + /* Toggles desktop menu. */ + CMD_EVENT_UI_COMPANION_TOGGLE, /* Toggles fullscreen mode. */ CMD_EVENT_FULLSCREEN_TOGGLE, CMD_EVENT_PERFCNT_REPORT_FRONTEND_LOG, @@ -271,7 +273,7 @@ void command_playlist_push_write( void command_playlist_update_write( void *data, size_t idx, - const char *path, + const char *path, const char *label, const char *core_path, const char *core_display_name, diff --git a/config.def.h b/config.def.h index 55653b7dc8..6d06b85b1a 100644 --- a/config.def.h +++ b/config.def.h @@ -670,6 +670,9 @@ static const bool ui_companion_start_on_boot = true; static const bool ui_companion_enable = false; +/* Currently only used to show the WIMP UI on startup */ +static const bool ui_companion_toggle = false; + #if defined(__QNX__) || defined(_XBOX1) || defined(_XBOX360) || defined(__CELLOS_LV2__) || (defined(__MACH__) && defined(IOS)) || defined(ANDROID) || defined(WIIU) || defined(HAVE_NEON) || defined(GEKKO) || defined(__ARM_NEON__) static enum resampler_quality audio_resampler_quality_level = RESAMPLER_QUALITY_LOWER; #elif defined(PSP) || defined(_3DS) || defined(VITA) diff --git a/config.def.keybinds.h b/config.def.keybinds.h index 298f9971a6..d941a210cf 100644 --- a/config.def.keybinds.h +++ b/config.def.keybinds.h @@ -97,6 +97,7 @@ static const struct retro_keybind retro_keybinds_1[] = { { true, RARCH_DISK_PREV, MENU_ENUM_LABEL_VALUE_INPUT_META_DISK_PREV, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RARCH_GRAB_MOUSE_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_GRAB_MOUSE_TOGGLE, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RARCH_GAME_FOCUS_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_GAME_FOCUS_TOGGLE, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE }, + { true, RARCH_UI_COMPANION_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_UI_COMPANION_TOGGLE, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RARCH_MENU_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_MENU_TOGGLE, RETROK_SPACE, NO_BTN, NO_BTN, 0, AXIS_NONE }, #else { true, RETRO_DEVICE_ID_JOYPAD_B, MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_B, RETROK_z, NO_BTN, NO_BTN, 0, AXIS_NONE }, @@ -172,6 +173,7 @@ static const struct retro_keybind retro_keybinds_1[] = { { true, RARCH_DISK_PREV, MENU_ENUM_LABEL_VALUE_INPUT_META_DISK_PREV, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RARCH_GRAB_MOUSE_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_GRAB_MOUSE_TOGGLE, RETROK_F11, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RARCH_GAME_FOCUS_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_GAME_FOCUS_TOGGLE, RETROK_SCROLLOCK, NO_BTN, NO_BTN, 0, AXIS_NONE }, + { true, RARCH_UI_COMPANION_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_UI_COMPANION_TOGGLE, RETROK_F5, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RARCH_MENU_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_MENU_TOGGLE, RETROK_F1, NO_BTN, NO_BTN, 0, AXIS_NONE }, #endif }; diff --git a/configuration.c b/configuration.c index 4e5da257f0..faf86ca26f 100644 --- a/configuration.c +++ b/configuration.c @@ -1180,6 +1180,7 @@ static struct config_bool_setting *populate_settings_bool(settings_t *settings, SETTING_BOOL("automatically_add_content_to_playlist", &settings->bools.automatically_add_content_to_playlist, true, automatically_add_content_to_playlist, false); SETTING_BOOL("ui_companion_start_on_boot", &settings->bools.ui_companion_start_on_boot, true, ui_companion_start_on_boot, false); SETTING_BOOL("ui_companion_enable", &settings->bools.ui_companion_enable, true, ui_companion_enable, false); + SETTING_BOOL("ui_companion_toggle", &settings->bools.ui_companion_toggle, false, ui_companion_toggle, false); SETTING_BOOL("video_gpu_record", &settings->bools.video_gpu_record, true, gpu_record, false); SETTING_BOOL("input_remap_binds_enable", &settings->bools.input_remap_binds_enable, true, true, false); SETTING_BOOL("all_users_control_menu", &settings->bools.input_all_users_control_menu, true, all_users_control_menu, false); @@ -3526,14 +3527,14 @@ static void save_keybind_mbutton(config_file_t *conf, const char *base, const struct retro_keybind *bind, bool save_empty) { - char key[64]; + char key[64]; - key[0] = '\0'; + key[0] = '\0'; - fill_pathname_join_delim_concat(key, prefix, - base, '_', "_mbtn", sizeof(key)); + fill_pathname_join_delim_concat(key, prefix, + base, '_', "_mbtn", sizeof(key)); - switch ( bind->mbutton ) + switch ( bind->mbutton ) { case RETRO_DEVICE_ID_MOUSE_LEFT: config_set_uint64(conf, key, 1); diff --git a/configuration.h b/configuration.h index affea22fa7..a1ed6436dd 100644 --- a/configuration.h +++ b/configuration.h @@ -198,6 +198,7 @@ typedef struct settings bool ui_suspend_screensaver_enable; bool ui_companion_start_on_boot; bool ui_companion_enable; + bool ui_companion_toggle; /* Cheevos */ bool cheevos_enable; diff --git a/core_info.c b/core_info.c index d7ed2041d6..18bcfeffec 100644 --- a/core_info.c +++ b/core_info.c @@ -151,6 +151,7 @@ static void core_info_list_free(core_info_list_t *core_info_list) free(info->systemname); free(info->system_manufacturer); free(info->display_name); + free(info->display_version); free(info->supported_extensions); free(info->authors); free(info->permissions); @@ -278,6 +279,13 @@ static core_info_list_t *core_info_list_new(const char *path, free(tmp); tmp = NULL; } + if (config_get_string(conf, "display_version", &tmp) + && !string_is_empty(tmp)) + { + core_info[i].display_version = strdup(tmp); + free(tmp); + tmp = NULL; + } if (config_get_string(conf, "corename", &tmp) && !string_is_empty(tmp)) { diff --git a/core_info.h b/core_info.h index b63fa8df98..f87805e605 100644 --- a/core_info.h +++ b/core_info.h @@ -43,6 +43,7 @@ typedef struct char *path; void *config_data; char *display_name; + char *display_version; char *core_name; char *system_manufacturer; char *systemname; diff --git a/frontend/frontend.c b/frontend/frontend.c index bd2fecde6e..8dd0b71693 100644 --- a/frontend/frontend.c +++ b/frontend/frontend.c @@ -37,6 +37,13 @@ #include "../paths.h" #include "../retroarch.h" +/* griffin hack */ +#ifdef HAVE_QT +#ifndef HAVE_MAIN +#define HAVE_MAIN +#endif +#endif + #ifndef HAVE_MAIN #include "../retroarch.h" #endif @@ -98,6 +105,9 @@ void main_exit(void *args) int rarch_main(int argc, char *argv[], void *data) { void *args = (void*)data; +#ifdef HAVE_MAIN + const ui_application_t *ui_application = NULL; +#endif rarch_ctl(RARCH_CTL_PREINIT, NULL); frontend_driver_init_first(args); @@ -140,6 +150,11 @@ int rarch_main(int argc, char *argv[], void *data) }while(1); main_exit(args); +#elif HAVE_QT + ui_application = ui_companion_driver_get_qt_application_ptr(); + + if (ui_application && ui_application->run) + ui_application->run(args); #endif return 0; diff --git a/gfx/common/win32_common.c b/gfx/common/win32_common.c index b29359a8ce..23951682e4 100644 --- a/gfx/common/win32_common.c +++ b/gfx/common/win32_common.c @@ -434,7 +434,7 @@ static int win32_drag_query_file(HWND hwnd, WPARAM wparam) #ifndef _XBOX static LRESULT win32_handle_keyboard_event(HWND hwnd, UINT message, - WPARAM wparam, LPARAM lparam) + WPARAM wparam, LPARAM lparam) { uint16_t mod = 0; @@ -818,7 +818,7 @@ bool win32_window_create(void *data, unsigned style, notification_filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; notification_filter.dbcc_classguid = GUID_DEVINTERFACE_HID; notification_handler = RegisterDeviceNotification( - main_window.hwnd, ¬ification_filter, DEVICE_NOTIFY_WINDOW_HANDLE); + main_window.hwnd, ¬ification_filter, DEVICE_NOTIFY_WINDOW_HANDLE); if (!notification_handler) RARCH_ERR("Error registering for notifications\n"); @@ -1029,7 +1029,7 @@ void win32_set_style(MONITORINFOEX *current_mon, HMONITOR *hm_to_use, float refresh_mod = settings->bools.video_black_frame_insertion ? 2.0f : 1.0f; unsigned refresh = roundf(settings->floats.video_refresh_rate * refresh_mod * settings->uints.video_swap_interval); - + if (windowed_full) { *style = WS_EX_TOPMOST | WS_POPUP; diff --git a/gfx/drivers_context/x_ctx.c b/gfx/drivers_context/x_ctx.c index bc906021bb..aa097bb6cc 100644 --- a/gfx/drivers_context/x_ctx.c +++ b/gfx/drivers_context/x_ctx.c @@ -124,7 +124,7 @@ typedef struct Hints } Hints; /* We use long because X11 wants 32-bit pixels for 32-bit systems and 64 for 64... */ -const unsigned long retroarch_icon_data[] = { +static const unsigned long retroarch_icon_data[] = { 16, 16, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, diff --git a/gfx/video_driver.c b/gfx/video_driver.c index acd5eb8152..1776954fef 100644 --- a/gfx/video_driver.c +++ b/gfx/video_driver.c @@ -229,6 +229,8 @@ static void *video_context_data = NULL; static bool deferred_video_context_driver_set_flags = false; static gfx_ctx_flags_t deferred_flag_data = {0}; +static bool video_started_fullscreen = false; + static shader_backend_t *current_shader = NULL; static void *current_shader_data = NULL; @@ -422,6 +424,11 @@ static const shader_backend_t *shader_ctx_drivers[] = { NULL }; +bool video_driver_started_fullscreen(void) +{ + return video_started_fullscreen; +} + /* Stub functions */ static void update_window_title_null(void *data, void *data2) @@ -1021,6 +1028,8 @@ static bool video_driver_init_internal(bool *video_is_threaded) (video_driver_pix_fmt == RETRO_PIXEL_FORMAT_XRGB8888); video.parent = 0; + video_started_fullscreen = video.fullscreen; + /* Reset video frame count */ video_driver_frame_count = 0; @@ -1631,7 +1640,7 @@ bool video_driver_supports_viewport_read(void) bool video_driver_supports_read_frame_raw(void) { if (current_video->read_frame_raw) - return true; + return true; return false; } @@ -2562,11 +2571,11 @@ void video_driver_frame(const void *data, unsigned width, compute_audio_buffer_statistics(&audio_stats); snprintf(video_info.stat_text, - sizeof(video_info.stat_text), + sizeof(video_info.stat_text), "Video Statistics:\n -Frame rate: %6.2f fps\n -Frame time: %6.2f ms\n -Frame time deviation: %.3f %%\n" " -Frame count: %" PRIu64"\n -Viewport: %d x %d x %3.2f\n" "Audio Statistics:\n -Average buffer saturation: %.2f %%\n -Standard deviation: %.2f %%\n -Time spent close to underrun: %.2f %%\n -Time spent close to blocking: %.2f %%\n -Sample count: %d\n" - "Core Geometry:\n -Size: %u x %u\n -Max Size: %u x %u\n -Aspect: %3.2f\nCore Timing:\n -FPS: %3.2f\n -Sample Rate: %6.2f\n", + "Core Geometry:\n -Size: %u x %u\n -Max Size: %u x %u\n -Aspect: %3.2f\nCore Timing:\n -FPS: %3.2f\n -Sample Rate: %6.2f\n", video_info.frame_rate, video_info.frame_time, 100.0 * stddev, @@ -3336,7 +3345,7 @@ bool video_context_driver_set_flags(gfx_ctx_flags_t *flags) enum gfx_ctx_api video_context_driver_get_api(void) { - enum gfx_ctx_api ctx_api = video_context_data ? + enum gfx_ctx_api ctx_api = video_context_data ? current_video_context.get_api(video_context_data) : GFX_CTX_NONE; if (ctx_api == GFX_CTX_NONE) @@ -3436,7 +3445,7 @@ void video_shader_driver_use(void *data) { if (current_shader && current_shader->use) { - video_shader_ctx_info_t *shader_info = + video_shader_ctx_info_t *shader_info = (video_shader_ctx_info_t*)data; current_shader->use(shader_info->data, current_shader_data, shader_info->idx, shader_info->set_active); diff --git a/gfx/video_driver.h b/gfx/video_driver.h index 4563e9a3b7..5cb44cf763 100644 --- a/gfx/video_driver.h +++ b/gfx/video_driver.h @@ -1347,6 +1347,8 @@ float video_driver_get_refresh_rate(void); extern bool (*video_driver_cb_has_focus)(void); +bool video_driver_started_fullscreen(void); + extern video_driver_t video_gl; extern video_driver_t video_vulkan; extern video_driver_t video_psp1; diff --git a/griffin/griffin_cpp.cpp b/griffin/griffin_cpp.cpp index ac200c1f80..562d395a00 100644 --- a/griffin/griffin_cpp.cpp +++ b/griffin/griffin_cpp.cpp @@ -33,6 +33,7 @@ MENU UI ============================================================ */ #if defined(HAVE_QT) +#define HAVE_MAIN /* also requires defining in frontend.c */ #include "../ui/drivers/ui_qt.cpp" #include "../ui/drivers/qt/ui_qt_window.cpp" @@ -41,10 +42,6 @@ UI #include "../ui/drivers/qt/ui_qt_application.cpp" #endif -#if defined(HAVE_QT_WRAPPER) -#include "../ui/drivers/ui_qt.cpp" -#endif - /*============================================================ VIDEO DRIVER ============================================================ */ diff --git a/input/input_defines.h b/input/input_defines.h index 12e2be7e97..5e4103bcc9 100644 --- a/input/input_defines.h +++ b/input/input_defines.h @@ -53,19 +53,19 @@ enum RARCH_ANALOG_RIGHT_Y_MINUS, RARCH_ANALOG_BIND_LIST_END, - /* Lightgun */ - RARCH_LIGHTGUN_TRIGGER = RARCH_FIRST_LIGHTGUN_BIND, - RARCH_LIGHTGUN_RELOAD, - RARCH_LIGHTGUN_AUX_A, - RARCH_LIGHTGUN_AUX_B, - RARCH_LIGHTGUN_AUX_C, - RARCH_LIGHTGUN_START, - RARCH_LIGHTGUN_SELECT, - RARCH_LIGHTGUN_DPAD_UP, - RARCH_LIGHTGUN_DPAD_DOWN, - RARCH_LIGHTGUN_DPAD_LEFT, - RARCH_LIGHTGUN_DPAD_RIGHT, - RARCH_LIGHTGUN_BIND_LIST_END, + /* Lightgun */ + RARCH_LIGHTGUN_TRIGGER = RARCH_FIRST_LIGHTGUN_BIND, + RARCH_LIGHTGUN_RELOAD, + RARCH_LIGHTGUN_AUX_A, + RARCH_LIGHTGUN_AUX_B, + RARCH_LIGHTGUN_AUX_C, + RARCH_LIGHTGUN_START, + RARCH_LIGHTGUN_SELECT, + RARCH_LIGHTGUN_DPAD_UP, + RARCH_LIGHTGUN_DPAD_DOWN, + RARCH_LIGHTGUN_DPAD_LEFT, + RARCH_LIGHTGUN_DPAD_RIGHT, + RARCH_LIGHTGUN_BIND_LIST_END, /* Turbo */ RARCH_TURBO_ENABLE = RARCH_FIRST_MISC_CUSTOM_BIND, @@ -107,6 +107,7 @@ enum RARCH_DISK_PREV, RARCH_GRAB_MOUSE_TOGGLE, RARCH_GAME_FOCUS_TOGGLE, + RARCH_UI_COMPANION_TOGGLE, RARCH_MENU_TOGGLE, diff --git a/input/input_driver.c b/input/input_driver.c index 742b34b1b9..f51faf1c51 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -296,17 +296,17 @@ const struct input_bind_map input_config_bind_map[RARCH_BIND_LIST_END_NULL] = { DECLARE_BIND(r_y_plus, RARCH_ANALOG_RIGHT_Y_PLUS, MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_RIGHT_Y_PLUS), DECLARE_BIND(r_y_minus, RARCH_ANALOG_RIGHT_Y_MINUS, MENU_ENUM_LABEL_VALUE_INPUT_ANALOG_RIGHT_Y_MINUS), - DECLARE_BIND( gun_trigger, RARCH_LIGHTGUN_TRIGGER, MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_TRIGGER ), - DECLARE_BIND( gun_offscreen_shot, RARCH_LIGHTGUN_RELOAD, MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_RELOAD ), - DECLARE_BIND( gun_aux_a, RARCH_LIGHTGUN_AUX_A, MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_AUX_A ), - DECLARE_BIND( gun_aux_b, RARCH_LIGHTGUN_AUX_B, MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_AUX_B ), - DECLARE_BIND( gun_aux_c, RARCH_LIGHTGUN_AUX_C, MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_AUX_C ), - DECLARE_BIND( gun_start, RARCH_LIGHTGUN_START, MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_START ), - DECLARE_BIND( gun_select, RARCH_LIGHTGUN_SELECT, MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_SELECT ), - DECLARE_BIND( gun_dpad_up, RARCH_LIGHTGUN_DPAD_UP, MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_DPAD_UP ), - DECLARE_BIND( gun_dpad_down, RARCH_LIGHTGUN_DPAD_DOWN, MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_DPAD_DOWN ), - DECLARE_BIND( gun_dpad_left, RARCH_LIGHTGUN_DPAD_LEFT, MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_DPAD_LEFT ), - DECLARE_BIND( gun_dpad_right, RARCH_LIGHTGUN_DPAD_RIGHT, MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_DPAD_RIGHT ), + DECLARE_BIND( gun_trigger, RARCH_LIGHTGUN_TRIGGER, MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_TRIGGER ), + DECLARE_BIND( gun_offscreen_shot, RARCH_LIGHTGUN_RELOAD, MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_RELOAD ), + DECLARE_BIND( gun_aux_a, RARCH_LIGHTGUN_AUX_A, MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_AUX_A ), + DECLARE_BIND( gun_aux_b, RARCH_LIGHTGUN_AUX_B, MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_AUX_B ), + DECLARE_BIND( gun_aux_c, RARCH_LIGHTGUN_AUX_C, MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_AUX_C ), + DECLARE_BIND( gun_start, RARCH_LIGHTGUN_START, MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_START ), + DECLARE_BIND( gun_select, RARCH_LIGHTGUN_SELECT, MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_SELECT ), + DECLARE_BIND( gun_dpad_up, RARCH_LIGHTGUN_DPAD_UP, MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_DPAD_UP ), + DECLARE_BIND( gun_dpad_down, RARCH_LIGHTGUN_DPAD_DOWN, MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_DPAD_DOWN ), + DECLARE_BIND( gun_dpad_left, RARCH_LIGHTGUN_DPAD_LEFT, MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_DPAD_LEFT ), + DECLARE_BIND( gun_dpad_right, RARCH_LIGHTGUN_DPAD_RIGHT, MENU_ENUM_LABEL_VALUE_INPUT_LIGHTGUN_DPAD_RIGHT ), DECLARE_BIND(turbo, RARCH_TURBO_ENABLE, MENU_ENUM_LABEL_VALUE_INPUT_TURBO_ENABLE), @@ -343,6 +343,7 @@ const struct input_bind_map input_config_bind_map[RARCH_BIND_LIST_END_NULL] = { DECLARE_META_BIND(2, disk_prev, RARCH_DISK_PREV, MENU_ENUM_LABEL_VALUE_INPUT_META_DISK_PREV), DECLARE_META_BIND(2, grab_mouse_toggle, RARCH_GRAB_MOUSE_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_GRAB_MOUSE_TOGGLE), DECLARE_META_BIND(2, game_focus_toggle, RARCH_GAME_FOCUS_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_GAME_FOCUS_TOGGLE), + DECLARE_META_BIND(2, desktop_menu_toggle, RARCH_UI_COMPANION_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_UI_COMPANION_TOGGLE), #ifdef HAVE_MENU DECLARE_META_BIND(1, menu_toggle, RARCH_MENU_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_MENU_TOGGLE), #endif @@ -1001,9 +1002,10 @@ void input_menu_keys_pressed(void *data, input_bits_t *p_new_state) if (!menu_input_dialog_get_display_kb()) { - unsigned ids[14][2]; + unsigned ids[15][2]; const struct retro_keybind *quitkey = &input_config_binds[0][RARCH_QUIT_KEY]; const struct retro_keybind *fskey = &input_config_binds[0][RARCH_FULLSCREEN_TOGGLE_KEY]; + const struct retro_keybind *companionkey = &input_config_binds[0][RARCH_UI_COMPANION_TOGGLE]; ids[0][0] = RETROK_SPACE; ids[0][1] = RETRO_DEVICE_ID_JOYPAD_START; @@ -1033,6 +1035,8 @@ void input_menu_keys_pressed(void *data, input_bits_t *p_new_state) ids[12][1] = RETRO_DEVICE_ID_JOYPAD_A; ids[13][0] = RETROK_DELETE; ids[13][1] = RETRO_DEVICE_ID_JOYPAD_Y; + ids[14][0] = companionkey->key; + ids[14][1] = RARCH_UI_COMPANION_TOGGLE; if (settings->bools.input_menu_swap_ok_cancel_buttons) { @@ -1040,7 +1044,7 @@ void input_menu_keys_pressed(void *data, input_bits_t *p_new_state) ids[12][1] = RETRO_DEVICE_ID_JOYPAD_B; } - for (i = 0; i < 14; i++) + for (i = 0; i < 15; i++) { if (current_input->input_state(current_input_data, joypad_info, binds, 0, @@ -1092,7 +1096,7 @@ void input_keys_pressed(void *data, input_bits_t *p_new_state) if (check_input_driver_block_hotkey(binds_norm, binds_auto)) { - const struct retro_keybind *enable_hotkey = + const struct retro_keybind *enable_hotkey = &input_config_binds[0][RARCH_ENABLE_HOTKEY]; if ( enable_hotkey && enable_hotkey->valid @@ -1106,9 +1110,9 @@ void input_keys_pressed(void *data, input_bits_t *p_new_state) if (binds[RARCH_GAME_FOCUS_TOGGLE].valid) { - const struct retro_keybind *focus_binds_auto = + const struct retro_keybind *focus_binds_auto = &input_autoconf_binds[0][RARCH_GAME_FOCUS_TOGGLE]; - const struct retro_keybind *focus_normal = + const struct retro_keybind *focus_normal = &binds[RARCH_GAME_FOCUS_TOGGLE]; /* Allows rarch_focus_toggle hotkey to still work @@ -1172,7 +1176,7 @@ void input_get_state_for_port(void *data, unsigned port, input_bits_t *p_new_sta for (j = 0; j < 2; j++) { unsigned offset = 0 + (i * 4) + (j * 2); - int16_t val = input_joypad_analog(joypad_driver, + int16_t val = input_joypad_analog(joypad_driver, joypad_info, port, i, j, libretro_input_binds[port]); if (val >= 0) @@ -2761,9 +2765,9 @@ unsigned input_config_get_device_count() unsigned num_devices; for ( num_devices = 0; num_devices < MAX_INPUT_DEVICES; ++num_devices ) { - const char *device_name = input_config_get_device_name(num_devices); - if ( string_is_empty(device_name) ) - break; + const char *device_name = input_config_get_device_name(num_devices); + if ( string_is_empty(device_name) ) + break; } return num_devices; } @@ -2797,7 +2801,7 @@ void input_config_set_device_name(unsigned port, const char *name) name, sizeof(input_device_names[port])); - input_autoconfigure_joypad_reindex_devices(); + input_autoconfigure_joypad_reindex_devices(); } } diff --git a/intl/msg_hash_ja.h b/intl/msg_hash_ja.h index e5eb01c715..efe5596d93 100644 --- a/intl/msg_hash_ja.h +++ b/intl/msg_hash_ja.h @@ -885,6 +885,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_GRAB_MOUSE_TOGGLE, "マウスグラブを切り替え") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_GAME_FOCUS_TOGGLE, "ゲームのフォーカスを切り替え") +MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_UI_COMPANION_TOGGLE, + "デスクトップメニューを切り替え") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_LOAD_STATE_KEY, "保存状態をロード") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MENU_TOGGLE, @@ -1631,6 +1633,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_UI_COMPANION_ENABLE, "UI Companion Enable") MSG_HASH(MENU_ENUM_LABEL_VALUE_UI_COMPANION_START_ON_BOOT, "UI Companion Start On Boot") +MSG_HASH(MENU_ENUM_LABEL_VALUE_UI_COMPANION_TOGGLE, + "起動時にデスクトップメニューを表示") MSG_HASH(MENU_ENUM_LABEL_VALUE_UI_MENUBAR_ENABLE, "メニューバー") MSG_HASH(MENU_ENUM_LABEL_VALUE_UNABLE_TO_READ_COMPRESSED_FILE, @@ -2124,7 +2128,7 @@ MSG_HASH(MSG_FAILED_TO_LOAD_OVERLAY, MSG_HASH(MSG_FAILED_TO_LOAD_STATE, "Failed to load state from") MSG_HASH(MSG_FAILED_TO_OPEN_LIBRETRO_CORE, - "Failed to open libretro core") + "コアをロードするに失敗しました") MSG_HASH(MSG_FAILED_TO_PATCH, "パッチに失敗しました") MSG_HASH(MSG_FAILED_TO_RECEIVE_HEADER_FROM_CLIENT, @@ -3332,9 +3336,9 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, "Enable background filler thickness") MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For 15 kHz CRT displays only. Attempts to use exact core/game resolution and refresh rate.") -MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") -MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_REWIND, "Show Rewind Settings") MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_REWIND, @@ -3355,3 +3359,111 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_MIXER_SETTINGS, "Mixer Settings") MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_MIXER_SETTINGS, "View and/or modify audio mixer settings.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_INFO, + "詳細") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_FILE, + "ファイル(&F)") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_FILE_LOAD_CORE, + "コアをロード(&L)...") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_FILE_UNLOAD_CORE, + "コアをアンロード(&U)") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_FILE_EXIT, + "終了(&X)") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_EDIT, + "編集(&E)") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_EDIT_SEARCH, + "検索(&S)") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW, + "表示(&V)") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_CLOSED_DOCKS, + "閉じたドック") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS, + "設定(&O)...") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_SAVE_DOCK_POSITIONS, + "ドック配置を記憶:") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_SAVE_GEOMETRY, + "ウィンドウ位置とサイズを記憶:") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_SAVE_LAST_TAB, + "最後のコンテンツブラウザーのタブを記憶:") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THEME, + "テーマ") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THEME_SYSTEM_DEFAULT, + "<システムデフォルト>") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THEME_DARK, + "ダーク") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THEME_CUSTOM, + "カスタム...") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_TITLE, + "設定") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_NAME, + "名前") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_LOAD_CUSTOM_CORE, + "カスタムなコアをロード...") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_LOAD_CORE, + "コアをロード") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_LOADING_CORE, + "コアをロード中...") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_CORE_VERSION, + "バージョン") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_TAB_PLAYLISTS, + "プレイリスト") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_TAB_FILE_BROWSER, + "ファイルブラウザー") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_TAB_FILE_BROWSER_TOP, + "先頭") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_TAB_FILE_BROWSER_UP, + "上へ") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_DOCK_CONTENT_BROWSER, + "コンテンツブラウザー") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_BOXART, + "ボックスアート") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_SCREENSHOT, + "スクリーンショット") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_TITLE_SCREEN, + "タイトルスクリーン") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_ALL_PLAYLISTS, + "すべてのプレイリスト") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_CORE, + "コア") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_CORE_INFO, + "コア情報") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_CORE_SELECTION_ASK, + "<問い合わせる>") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_INFORMATION, + "情報") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_WARNING, + "警告") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_ERROR, + "エラー") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_RESTART_TO_TAKE_EFFECT, + "変更はRetroArchを再起動した後に反映されます。") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_LOG, + "ログ") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_DONT_SHOW_AGAIN, + "今後表示しない") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_STOP, + "停止") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_ASSOCIATE_CORE, + "コアに関連付ける") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_HIDDEN_PLAYLISTS, + "隠したプレイリスト") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_HIDE, + "隠す") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_HIGHLIGHT_COLOR, + "ハイライトカラー") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_CHOOSE, + "選択(&C)...") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_SELECT_COLOR, + "色の選択") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_SELECT_THEME, + "テーマの選択") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_CUSTOM_THEME, + "カスタムなテーマ") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_FILE_PATH_IS_BLANK, + "ファイルのパスは空きです。") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_FILE_IS_EMPTY, + "ファイルは空きです。") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_FILE_READ_OPEN_FAILED, + "ファイルを読み込みのために開けません。") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_FILE_DOES_NOT_EXIST, + "ファイルは存在しません。") diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index 2223b11108..3f3464e66c 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -1057,6 +1057,8 @@ MSG_HASH(MENU_ENUM_LABEL_UI_COMPANION_ENABLE, "ui_companion_enable") MSG_HASH(MENU_ENUM_LABEL_UI_COMPANION_START_ON_BOOT, "ui_companion_start_on_boot") +MSG_HASH(MENU_ENUM_LABEL_UI_COMPANION_TOGGLE, + "ui_companion_toggle") MSG_HASH(MENU_ENUM_LABEL_UI_MENUBAR_ENABLE, "ui_menubar_enable") MSG_HASH(MENU_ENUM_LABEL_UNDO_LOAD_STATE, diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 81269e52d7..a249694c81 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -871,6 +871,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_GRAB_MOUSE_TOGGLE, "Grab mouse toggle") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_GAME_FOCUS_TOGGLE, "Game focus toggle") +MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_UI_COMPANION_TOGGLE, + "Desktop menu toggle") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_LOAD_STATE_KEY, "Load state") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MENU_TOGGLE, @@ -1657,6 +1659,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_UI_COMPANION_ENABLE, "UI Companion Enable") MSG_HASH(MENU_ENUM_LABEL_VALUE_UI_COMPANION_START_ON_BOOT, "UI Companion Start On Boot") +MSG_HASH(MENU_ENUM_LABEL_VALUE_UI_COMPANION_TOGGLE, + "Show desktop menu on startup") MSG_HASH(MENU_ENUM_LABEL_VALUE_UI_MENUBAR_ENABLE, "Menubar") MSG_HASH(MENU_ENUM_LABEL_VALUE_UNABLE_TO_READ_COMPRESSED_FILE, @@ -2034,7 +2038,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_HELP_LIST, MSG_HASH(MSG_ADDED_TO_FAVORITES, "Added to favorites") MSG_HASH(MSG_RESET_CORE_ASSOCIATION, - "Playlist entry core association has been reset.") + "Playlist entry core association has been reset.") MSG_HASH(MSG_APPENDED_DISK, "Appended disk") MSG_HASH(MSG_APPLICATION_DIR, @@ -3490,10 +3494,14 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BORDER_FILLER_THICKNESS_ENABLE, "Enable border filler thickness") MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_RGUI_BACKGROUND_FILLER_THICKNESS_ENABLE, "Enable background filler thickness") -MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, "For 15 kHz CRT displays only. Attempts to use exact core/game resolution and refresh rate.") -MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, "CRT SwitchRes") -MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") -MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, "CRT Super Resolution") +MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION, + "For 15 kHz CRT displays only. Attempts to use exact core/game resolution and refresh rate.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION, + "CRT SwitchRes") +MSG_HASH(MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_SUPER, + "When CRT SwitchRes is enabled, force ultrawide horizontal resolution to minimize mode switching.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_SUPER, + "CRT Super Resolution") MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_REWIND, "Show Rewind Settings") MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_SHOW_REWIND, @@ -3514,3 +3522,119 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_AUDIO_MIXER_SETTINGS, "Mixer Settings") MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_MIXER_SETTINGS, "View and/or modify audio mixer settings.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_INFO, + "Info") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_FILE, + "&File") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_FILE_LOAD_CORE, + "&Load Core...") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_FILE_UNLOAD_CORE, + "&Unload Core") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_FILE_EXIT, + "E&xit") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_EDIT, + "&Edit") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_EDIT_SEARCH, + "&Search") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW, + "&View") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_CLOSED_DOCKS, + "Closed Docks") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS, + "&Options...") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_SAVE_DOCK_POSITIONS, + "Remember dock positions:") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_SAVE_GEOMETRY, + "Remember window geometry:") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_SAVE_LAST_TAB, + "Remember last content browser tab:") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THEME, + "Theme") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THEME_SYSTEM_DEFAULT, + "") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THEME_DARK, + "Dark") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THEME_CUSTOM, + "Custom...") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_TITLE, + "Options") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_LOAD_CUSTOM_CORE, + "Load Custom Core...") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_LOAD_CORE, + "Load Core") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_LOADING_CORE, + "Loading Core...") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_NAME, + "Name") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_CORE_VERSION, + "Version") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_TAB_PLAYLISTS, + "Playlists") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_TAB_FILE_BROWSER, + "File Browser") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_TAB_FILE_BROWSER_TOP, + "Top") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_TAB_FILE_BROWSER_UP, + "Up") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_DOCK_CONTENT_BROWSER, + "Content Browser") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_BOXART, + "Boxart") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_SCREENSHOT, + "Screenshot") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_TITLE_SCREEN, + "Title Screen") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_ALL_PLAYLISTS, + "All Playlists") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_CORE, + "Core") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_CORE_INFO, + "Core Info") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_CORE_SELECTION_ASK, + "") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_INFORMATION, + "Information") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_WARNING, + "Warning") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_ERROR, + "Error") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_RESTART_TO_TAKE_EFFECT, + "Please restart the program for the changes to take effect.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_LOG, + "Log") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_SCAN_FINISHED, + "Scan Finished.

\n" + "In order for content to be correctly scanned, you must:\n" + "\n" + "Finally, the content must match existing databases from here. If it is still not working, consider submitting a bug report.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_DONT_SHOW_AGAIN, + "Don't show this again") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_STOP, + "Stop") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_ASSOCIATE_CORE, + "Associate Core") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_HIDDEN_PLAYLISTS, + "Hidden Playlists") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_HIDE, + "Hide") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_HIGHLIGHT_COLOR, + "Highlight Color") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_CHOOSE, + "&Choose...") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_SELECT_COLOR, + "Select Color") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_SELECT_THEME, + "Select Theme") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_CUSTOM_THEME, + "Custom Theme") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_FILE_PATH_IS_BLANK, + "File path is blank.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_FILE_IS_EMPTY, + "File is empty.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_FILE_READ_OPEN_FAILED, + "Could not open file for reading.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_FILE_DOES_NOT_EXIST, + "File does not exist.") diff --git a/menu/cbs/menu_cbs_right.c b/menu/cbs/menu_cbs_right.c index decb95bed4..9d3b8ab9a5 100644 --- a/menu/cbs/menu_cbs_right.c +++ b/menu/cbs/menu_cbs_right.c @@ -57,8 +57,6 @@ static int generic_shader_action_parameter_right(struct video_shader_parameter * param->current += param->step; param->current = MIN(MAX(param->minimum, param->current), param->maximum); - if (ui_companion_is_on_foreground()) - ui_companion_driver_notify_refresh(); return 0; } @@ -74,7 +72,7 @@ int shader_action_parameter_right(unsigned type, const char *label, bool wraparo video_shader_driver_get_current_shader(&shader_info); param_prev = &shader_info.data->parameters[type - MENU_SETTINGS_SHADER_PARAMETER_0]; - param_menu = shader ? &shader->parameters[type - + param_menu = shader ? &shader->parameters[type - MENU_SETTINGS_SHADER_PARAMETER_0] : NULL; if (!param_prev || !param_menu) @@ -112,8 +110,8 @@ int action_right_input_desc_kbd(unsigned type, const char *label, if (!settings) return 0; - offset = type / ((MENU_SETTINGS_INPUT_DESC_KBD_END - - (MENU_SETTINGS_INPUT_DESC_KBD_END - + offset = type / ((MENU_SETTINGS_INPUT_DESC_KBD_END - + (MENU_SETTINGS_INPUT_DESC_KBD_END - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN))) - 1; id = (type / (offset + 1)) - MENU_SETTINGS_INPUT_DESC_KBD_BEGIN; @@ -162,7 +160,7 @@ int action_right_input_desc(unsigned type, const char *label, /* skip the not used buttons (unless they are at the end by calling the right desc function recursively also skip all the axes until analog remapping is implemented */ - if ((string_is_empty(system->input_desc_btn[user_idx][remap_idx]) && remap_idx < RARCH_CUSTOM_BIND_LIST_END) /*|| + if ((string_is_empty(system->input_desc_btn[user_idx][remap_idx]) && remap_idx < RARCH_CUSTOM_BIND_LIST_END) /*|| (remap_idx >= RARCH_FIRST_CUSTOM_BIND && remap_idx < RARCH_CUSTOM_BIND_LIST_END)*/) action_right_input_desc(type, label, wraparound); @@ -230,7 +228,7 @@ static int action_right_goto_tab(void) file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0); size_t selection = menu_navigation_get_selection(); menu_file_list_cbs_t *cbs = selection_buf ? (menu_file_list_cbs_t*) - file_list_get_actiondata_at_offset(selection_buf, selection) : NULL; + file_list_get_actiondata_at_offset(selection_buf, selection) : NULL; list_info.type = MENU_LIST_HORIZONTAL; list_info.action = MENU_ACTION_RIGHT; diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 3cd2b6f2dc..d792f4ddcc 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -2848,7 +2848,7 @@ static int menu_displaylist_parse_horizontal_content_actions( msg_hash_to_str(MENU_ENUM_LABEL_RUN), MENU_ENUM_LABEL_RUN, FILE_TYPE_PLAYLIST_ENTRY, 0, idx); - if (settings->bools.playlist_entry_rename && + if (settings->bools.playlist_entry_rename && !settings->bools.kiosk_mode_enable) menu_entries_append_enum(info->list, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_RENAME_ENTRY), @@ -2856,7 +2856,7 @@ static int menu_displaylist_parse_horizontal_content_actions( MENU_ENUM_LABEL_RENAME_ENTRY, FILE_TYPE_PLAYLIST_ENTRY, 0, idx); - if (settings->bools.playlist_entry_remove && + if (settings->bools.playlist_entry_remove && !settings->bools.kiosk_mode_enable) menu_entries_append_enum(info->list, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DELETE_ENTRY), @@ -3413,20 +3413,20 @@ static int menu_displaylist_parse_playlists( MENU_ENUM_LABEL_SCAN_FILE, MENU_SETTING_ACTION, 0, 0); #endif - if (settings->bools.menu_content_show_favorites) + if (settings->bools.menu_content_show_favorites) menu_entries_append_enum(info->list, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_GOTO_FAVORITES), msg_hash_to_str(MENU_ENUM_LABEL_GOTO_FAVORITES), MENU_ENUM_LABEL_GOTO_FAVORITES, MENU_SETTING_ACTION, 0, 0); - if (settings->bools.menu_content_show_images) + if (settings->bools.menu_content_show_images) menu_entries_append_enum(info->list, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_GOTO_IMAGES), msg_hash_to_str(MENU_ENUM_LABEL_GOTO_IMAGES), MENU_ENUM_LABEL_GOTO_IMAGES, MENU_SETTING_ACTION, 0, 0); - if (settings->bools.menu_content_show_music) + if (settings->bools.menu_content_show_music) menu_entries_append_enum(info->list, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_GOTO_MUSIC), msg_hash_to_str(MENU_ENUM_LABEL_GOTO_MUSIC), @@ -3434,7 +3434,7 @@ static int menu_displaylist_parse_playlists( MENU_SETTING_ACTION, 0, 0); #ifdef HAVE_FFMPEG - if (settings->bools.menu_content_show_video) + if (settings->bools.menu_content_show_video) menu_entries_append_enum(info->list, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_GOTO_VIDEO), msg_hash_to_str(MENU_ENUM_LABEL_GOTO_VIDEO), @@ -5550,7 +5550,11 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_UI_MENUBAR_ENABLE, PARSE_ONLY_BOOL, false); - +#ifdef HAVE_QT + menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_UI_COMPANION_TOGGLE, + PARSE_ONLY_BOOL, false); +#endif info->need_refresh = true; info->need_push = true; break; @@ -6433,21 +6437,21 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) MENU_ENUM_LABEL_FAVORITES, MENU_SETTING_ACTION, 0, 0); - if (settings->bools.menu_content_show_favorites) + if (settings->bools.menu_content_show_favorites) menu_entries_append_enum(info->list, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_GOTO_FAVORITES), msg_hash_to_str(MENU_ENUM_LABEL_GOTO_FAVORITES), MENU_ENUM_LABEL_GOTO_FAVORITES, MENU_SETTING_ACTION, 0, 0); - if (settings->bools.menu_content_show_images) + if (settings->bools.menu_content_show_images) menu_entries_append_enum(info->list, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_GOTO_IMAGES), msg_hash_to_str(MENU_ENUM_LABEL_GOTO_IMAGES), MENU_ENUM_LABEL_GOTO_IMAGES, MENU_SETTING_ACTION, 0, 0); - if (settings->bools.menu_content_show_music) + if (settings->bools.menu_content_show_music) menu_entries_append_enum(info->list, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_GOTO_MUSIC), msg_hash_to_str(MENU_ENUM_LABEL_GOTO_MUSIC), @@ -6455,7 +6459,7 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) MENU_SETTING_ACTION, 0, 0); #ifdef HAVE_FFMPEG - if (settings->bools.menu_content_show_video) + if (settings->bools.menu_content_show_video) menu_entries_append_enum(info->list, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_GOTO_VIDEO), msg_hash_to_str(MENU_ENUM_LABEL_GOTO_VIDEO), @@ -6626,7 +6630,7 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) if (frontend_driver_has_fork()) #endif { - if (settings->bools.menu_show_load_core) + if (settings->bools.menu_show_load_core) menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_CORE_LIST, PARSE_ACTION, false); } diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 5219aa4fce..91b772a945 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -4601,7 +4601,10 @@ static bool setting_append_list( { if (!input_config_bind_map_get_meta(i)) continue; - +#ifndef HAVE_QT + if (i == RARCH_UI_COMPANION_TOGGLE) + continue; +#endif CONFIG_BIND_ALT( list, list_info, &input_config_binds[0][i], @@ -5630,7 +5633,7 @@ static bool setting_append_list( general_write_handler, general_read_handler); menu_settings_list_current_add_range(list, list_info, 0, XMB_THEME_LAST-1, 1, true, true); - } + } #endif CONFIG_BOOL( list, list_info, @@ -5804,20 +5807,20 @@ static bool setting_append_list( SD_FLAG_NONE); settings_data_list_current_add_flags(list, list_info, SD_FLAG_LAKKA_ADVANCED); - CONFIG_STRING( - list, list_info, - settings->paths.menu_content_show_settings_password, - sizeof(settings->paths.menu_content_show_settings_password), - MENU_ENUM_LABEL_CONTENT_SHOW_SETTINGS_PASSWORD, - MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_SETTINGS_PASSWORD, - "", - &group_info, - &subgroup_info, - parent_group, - general_write_handler, - general_read_handler); - settings_data_list_current_add_flags(list, list_info, SD_FLAG_ALLOW_INPUT | SD_FLAG_LAKKA_ADVANCED); - } + CONFIG_STRING( + list, list_info, + settings->paths.menu_content_show_settings_password, + sizeof(settings->paths.menu_content_show_settings_password), + MENU_ENUM_LABEL_CONTENT_SHOW_SETTINGS_PASSWORD, + MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_SETTINGS_PASSWORD, + "", + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler); + settings_data_list_current_add_flags(list, list_info, SD_FLAG_ALLOW_INPUT | SD_FLAG_LAKKA_ADVANCED); + } #endif CONFIG_BOOL( @@ -6054,7 +6057,7 @@ static bool setting_append_list( general_write_handler, general_read_handler); menu_settings_list_current_add_range(list, list_info, 0, 3, 1, true, true); - + CONFIG_BOOL( list, list_info, &settings->bools.menu_xmb_vertical_thumbnails, @@ -6516,8 +6519,22 @@ static bool setting_append_list( general_read_handler, SD_FLAG_NONE); } - - +#ifdef HAVE_QT + CONFIG_BOOL( + list, list_info, + &settings->bools.ui_companion_toggle, + MENU_ENUM_LABEL_UI_COMPANION_TOGGLE, + MENU_ENUM_LABEL_VALUE_UI_COMPANION_TOGGLE, + ui_companion_toggle, + MENU_ENUM_LABEL_VALUE_OFF, + MENU_ENUM_LABEL_VALUE_ON, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + SD_FLAG_ADVANCED); +#endif END_SUB_GROUP(list, list_info, parent_group); END_GROUP(list, list_info, parent_group); break; @@ -6562,7 +6579,7 @@ static bool setting_append_list( END_SUB_GROUP(list, list_info, parent_group); - START_SUB_GROUP(list, list_info, "Playlist", &group_info, &subgroup_info, parent_group); + START_SUB_GROUP(list, list_info, "Playlist", &group_info, &subgroup_info, parent_group); CONFIG_BOOL( list, list_info, @@ -6579,7 +6596,7 @@ static bool setting_append_list( general_read_handler, SD_FLAG_NONE); - CONFIG_BOOL( + CONFIG_BOOL( list, list_info, &settings->bools.playlist_entry_remove, MENU_ENUM_LABEL_PLAYLIST_ENTRY_REMOVE, diff --git a/msg_hash.h b/msg_hash.h index 89314172c7..6eb87a259a 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -630,6 +630,7 @@ enum msg_hash_enums MENU_ENUM_LABEL_VALUE_INPUT_META_DISK_PREV, MENU_ENUM_LABEL_VALUE_INPUT_META_GRAB_MOUSE_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_GAME_FOCUS_TOGGLE, + MENU_ENUM_LABEL_VALUE_INPUT_META_UI_COMPANION_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_MENU_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_DEVICE_INDEX, @@ -806,6 +807,7 @@ enum msg_hash_enums MENU_LABEL(VIDEO_DISABLE_COMPOSITION), MENU_LABEL(UI_COMPANION_ENABLE), MENU_LABEL(UI_COMPANION_START_ON_BOOT), + MENU_LABEL(UI_COMPANION_TOGGLE), MENU_LABEL(UI_MENUBAR_ENABLE), MENU_ENUM_LABEL_FILE_CONFIG, @@ -1770,6 +1772,62 @@ enum msg_hash_enums MENU_LABEL(NETPLAY_MITM_SERVER), MENU_LABEL(VIDEO_WINDOW_SHOW_DECORATIONS), + MENU_ENUM_LABEL_VALUE_QT_INFO, + MENU_ENUM_LABEL_VALUE_QT_MENU_FILE, + MENU_ENUM_LABEL_VALUE_QT_MENU_FILE_LOAD_CORE, + MENU_ENUM_LABEL_VALUE_QT_MENU_FILE_UNLOAD_CORE, + MENU_ENUM_LABEL_VALUE_QT_MENU_FILE_EXIT, + MENU_ENUM_LABEL_VALUE_QT_MENU_EDIT, + MENU_ENUM_LABEL_VALUE_QT_MENU_EDIT_SEARCH, + MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW, + MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_CLOSED_DOCKS, + MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS, + MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_SAVE_DOCK_POSITIONS, + MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_SAVE_GEOMETRY, + MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_SAVE_LAST_TAB, + MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THEME, + MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THEME_SYSTEM_DEFAULT, + MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THEME_DARK, + MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THEME_CUSTOM, + MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_TITLE, + MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_HIGHLIGHT_COLOR, + MENU_ENUM_LABEL_VALUE_QT_MENU_DOCK_CONTENT_BROWSER, + MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_BOXART, + MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_SCREENSHOT, + MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_TITLE_SCREEN, + MENU_ENUM_LABEL_VALUE_QT_LOAD_CORE, + MENU_ENUM_LABEL_VALUE_QT_LOAD_CUSTOM_CORE, + MENU_ENUM_LABEL_VALUE_QT_LOADING_CORE, + MENU_ENUM_LABEL_VALUE_QT_NAME, + MENU_ENUM_LABEL_VALUE_QT_CORE_VERSION, + MENU_ENUM_LABEL_VALUE_QT_TAB_PLAYLISTS, + MENU_ENUM_LABEL_VALUE_QT_TAB_FILE_BROWSER, + MENU_ENUM_LABEL_VALUE_QT_TAB_FILE_BROWSER_TOP, + MENU_ENUM_LABEL_VALUE_QT_TAB_FILE_BROWSER_UP, + MENU_ENUM_LABEL_VALUE_QT_ALL_PLAYLISTS, + MENU_ENUM_LABEL_VALUE_QT_CORE, + MENU_ENUM_LABEL_VALUE_QT_CORE_INFO, + MENU_ENUM_LABEL_VALUE_QT_CORE_SELECTION_ASK, + MENU_ENUM_LABEL_VALUE_QT_INFORMATION, + MENU_ENUM_LABEL_VALUE_QT_WARNING, + MENU_ENUM_LABEL_VALUE_QT_ERROR, + MENU_ENUM_LABEL_VALUE_QT_RESTART_TO_TAKE_EFFECT, + MENU_ENUM_LABEL_VALUE_QT_LOG, + MENU_ENUM_LABEL_VALUE_QT_SCAN_FINISHED, + MENU_ENUM_LABEL_VALUE_QT_DONT_SHOW_AGAIN, + MENU_ENUM_LABEL_VALUE_QT_STOP, + MENU_ENUM_LABEL_VALUE_QT_ASSOCIATE_CORE, + MENU_ENUM_LABEL_VALUE_QT_HIDDEN_PLAYLISTS, + MENU_ENUM_LABEL_VALUE_QT_HIDE, + MENU_ENUM_LABEL_VALUE_QT_CHOOSE, + MENU_ENUM_LABEL_VALUE_QT_SELECT_COLOR, + MENU_ENUM_LABEL_VALUE_QT_SELECT_THEME, + MENU_ENUM_LABEL_VALUE_QT_CUSTOM_THEME, + MENU_ENUM_LABEL_VALUE_QT_FILE_PATH_IS_BLANK, + MENU_ENUM_LABEL_VALUE_QT_FILE_IS_EMPTY, + MENU_ENUM_LABEL_VALUE_QT_FILE_READ_OPEN_FAILED, + MENU_ENUM_LABEL_VALUE_QT_FILE_DOES_NOT_EXIST, + MSG_LAST }; diff --git a/qb/config.libs.sh b/qb/config.libs.sh index 701d8c162d..bbd0b314db 100644 --- a/qb/config.libs.sh +++ b/qb/config.libs.sh @@ -270,6 +270,26 @@ check_val '' PULSE -lpulse check_val '' SDL -lSDL SDL check_val '' SDL2 -lSDL2 SDL2 +if [ "$HAVE_QT" = "yes" ]; then + check_pkgconf QT5CORE Qt5Core 5.2 + check_pkgconf QT5GUI Qt5Gui 5.2 + check_pkgconf QT5WIDGETS Qt5Widgets 5.2 + #check_pkgconf QT5WEBENGINE Qt5WebEngine 5.4 + + check_val '' QT5CORE -lQt5Core QT5CORE + check_val '' QT5GUI -lQt5Gui QT5GUI + check_val '' QT5WIDGETS -lQt5Widgets QT5WIDGETS + #check_val '' QT5WEBENGINE -lQt5WebEngine QT5WEBENGINE + + if [ "$HAVE_QT5CORE" = "no" ] || [ "$HAVE_QT5GUI" = "no" ] || [ "$HAVE_QT5WIDGETS" = "no" ]; then + die 1 'Error: Qt support requested, but required libraries could not be found.' + fi + + #if [ "$HAVE_QT5WEBENGINE" = "no" ]; then + # die : 'Notice: Qt5WebEngine not found, disabling web browser support.' + #fi +fi + if [ "$HAVE_SDL2" = 'yes' ] && [ "$HAVE_SDL" = 'yes' ]; then die : 'Notice: SDL drivers will be replaced by SDL2 ones.' HAVE_SDL=no diff --git a/qb/config.params.sh b/qb/config.params.sh index 126744aabe..645e8a3fdf 100644 --- a/qb/config.params.sh +++ b/qb/config.params.sh @@ -102,7 +102,6 @@ HAVE_PARPORT=auto # Parallel port joypad support HAVE_IMAGEVIEWER=yes # Built-in image viewer support. HAVE_MMAP=auto # MMAP support HAVE_QT=no # Qt companion support -HAVE_QT_WRAPPER=no # Qt wrapper support HAVE_XSHM=no # XShm video driver support HAVE_CHEEVOS=yes # Retro Achievements HAVE_SHADERPIPELINE=yes # Additional shader-based pipelines diff --git a/qb/qb.comp.sh b/qb/qb.comp.sh index 147715443d..a0cced4537 100644 --- a/qb/qb.comp.sh +++ b/qb/qb.comp.sh @@ -83,6 +83,15 @@ if [ "$OS" = "Win32" ]; then echo "$echobuf ... $WINDRES" fi +if [ "$HAVE_QT" = "yes" ]; then + echobuf="Checking for moc" + if [ -z "$MOC" ]; then + MOC="$(exists "moc")" || MOC="" + [ -z "$MOC" ] && die 1 "$echobuf ... Not found. Exiting." + fi + echo "$echobuf ... $MOC" +fi + if [ -z "$PKG_CONF_PATH" ]; then PKG_CONF_PATH="none" for pkgconf in pkgconf pkg-config; do diff --git a/qb/qb.libs.sh b/qb/qb.libs.sh index 453de6a612..2cf00c5998 100644 --- a/qb/qb.libs.sh +++ b/qb/qb.libs.sh @@ -226,6 +226,7 @@ create_config_make() [ "$USE_LANG_CXX" = 'yes' ] && printf %s\\n "CXX = $CXX" "CXXFLAGS = $CXXFLAGS" printf %s\\n "WINDRES = $WINDRES" \ + "MOC = $MOC" \ "ASFLAGS = $ASFLAGS" \ "LDFLAGS = $LDFLAGS" \ "INCLUDE_DIRS = $INCLUDE_DIRS" \ diff --git a/retroarch.c b/retroarch.c index d8b52c09b0..788ee318af 100644 --- a/retroarch.c +++ b/retroarch.c @@ -222,6 +222,7 @@ static bool rarch_ups_pref = false; static bool rarch_bps_pref = false; static bool rarch_ips_pref = false; static bool rarch_patch_blocked = false; +static bool rarch_first_start = true; static bool runloop_force_nonblock = false; static bool runloop_paused = false; @@ -238,7 +239,9 @@ static bool runloop_remaps_game_active = false; static bool runloop_game_options_active = false; static bool runloop_missing_bios = false; static bool runloop_autosave = false; - +#ifdef HAVE_DYNAMIC +static bool core_set_on_cmdline = false; +#endif static rarch_system_info_t runloop_system; static struct retro_frame_time_callback runloop_frame_time; static retro_keyboard_event_t runloop_key_event = NULL; @@ -258,6 +261,13 @@ static retro_time_t frame_limit_last_time = 0.0; extern bool input_driver_flushing_input; +#ifdef HAVE_DYNAMIC +bool retroarch_core_set_on_cmdline(void) +{ + return core_set_on_cmdline; +} +#endif + #ifdef HAVE_THREADS void runloop_msg_queue_lock(void) { @@ -864,6 +874,11 @@ static void retroarch_parse_input_and_config(int argc, char *argv[]) { settings_t *settings = config_get_ptr(); + if (rarch_first_start) + { + core_set_on_cmdline = true; + } + path_clear(RARCH_PATH_CORE); strlcpy(settings->paths.directory_libretro, optarg, sizeof(settings->paths.directory_libretro)); @@ -876,6 +891,11 @@ static void retroarch_parse_input_and_config(int argc, char *argv[]) } else if (filestream_exists(optarg)) { + if (rarch_first_start) + { + core_set_on_cmdline = true; + } + rarch_ctl(RARCH_CTL_SET_LIBRETRO_PATH, optarg); retroarch_override_setting_set(RARCH_OVERRIDE_SETTING_LIBRETRO, NULL); @@ -1352,11 +1372,18 @@ bool retroarch_main_init(int argc, char *argv[]) rarch_error_on_init = false; rarch_is_inited = true; + if (rarch_first_start) + rarch_first_start = false; + return true; error: command_event(CMD_EVENT_CORE_DEINIT, NULL); rarch_is_inited = false; + + if (rarch_first_start) + rarch_first_start = false; + return false; } @@ -1821,8 +1848,6 @@ bool rarch_ctl(enum rarch_ctl_state state, void *data) if (!idx) return false; core_option_manager_prev(runloop_core_options, *idx); - if (ui_companion_is_on_foreground()) - ui_companion_driver_notify_refresh(); } break; case RARCH_CTL_CORE_OPTION_NEXT: @@ -1831,8 +1856,6 @@ bool rarch_ctl(enum rarch_ctl_state state, void *data) if (!idx) return false; core_option_manager_next(runloop_core_options, *idx); - if (ui_companion_is_on_foreground()) - ui_companion_driver_notify_refresh(); } break; case RARCH_CTL_CORE_OPTIONS_GET: @@ -2322,13 +2345,8 @@ void runloop_msg_queue_push(const char *msg, msg_queue_push(runloop_msg_queue, msg_info.msg, msg_info.prio, msg_info.duration); - if (ui_companion_is_on_foreground()) - { - const ui_companion_driver_t *ui = ui_companion_get_ptr(); - if (ui->msg_queue_push) - ui->msg_queue_push(msg_info.msg, - msg_info.prio, msg_info.duration, msg_info.flush); - } + ui_companion_driver_msg_queue_push(msg_info.msg, + msg_info.prio, msg_info.duration, msg_info.flush); } #ifdef HAVE_THREADS @@ -2452,10 +2470,10 @@ static enum runloop_state runloop_check_state( #ifdef HAVE_MENU if (menu_is_alive && !(settings->bools.menu_unified_controls && !menu_input_dialog_get_display_kb())) - input_menu_keys_pressed(settings, ¤t_input); + input_menu_keys_pressed(settings, ¤t_input); else #endif - input_keys_pressed(settings, ¤t_input); + input_keys_pressed(settings, ¤t_input); #ifdef HAVE_MENU last_input = current_input; @@ -2659,7 +2677,6 @@ static enum runloop_state runloop_check_state( if (runloop_idle) return RUNLOOP_STATE_SLEEP; - /* Check game focus toggle */ { static bool old_pressed = false; @@ -2672,6 +2689,20 @@ static enum runloop_state runloop_check_state( old_pressed = pressed; } + /* Check UI companion toggle */ + { + static bool old_pressed = false; + bool pressed = BIT256_GET( + current_input, RARCH_UI_COMPANION_TOGGLE); + + if (pressed && !old_pressed) + { + command_event(CMD_EVENT_UI_COMPANION_TOGGLE, (void*)(intptr_t)0); + } + + old_pressed = pressed; + } + #ifdef HAVE_MENU /* Check menu toggle */ { diff --git a/retroarch.cfg b/retroarch.cfg index 019c14c326..d1a3250ccf 100644 --- a/retroarch.cfg +++ b/retroarch.cfg @@ -729,6 +729,9 @@ video_message_bgcolor_opacity = 1.0 # Start UI companion driver's interface on boot (if available). # ui_companion_start_on_boot = true +# Toggle companion UI on startup (currently only used to show the WIMP UI) +# ui_companion_toggle = false + #### Camera # Override the default camera device the camera driver uses. This is driver dependant. diff --git a/retroarch.h b/retroarch.h index 63c37428f1..02abfae515 100644 --- a/retroarch.h +++ b/retroarch.h @@ -357,6 +357,10 @@ void runloop_msg_queue_lock(void); void runloop_msg_queue_unlock(void); #endif +#ifdef HAVE_DYNAMIC +bool retroarch_core_set_on_cmdline(void); +#endif + RETRO_END_DECLS #endif diff --git a/tasks/task_database.c b/tasks/task_database.c index 5e9fbdec46..285cef0118 100644 --- a/tasks/task_database.c +++ b/tasks/task_database.c @@ -38,7 +38,7 @@ #include "../retroarch.h" #endif #include "../verbosity.h" - +#include "../ui/ui_companion_driver.h" #ifndef COLLECTION_SIZE #define COLLECTION_SIZE 99999 #endif @@ -1264,6 +1264,7 @@ static void task_database_handler(retro_task_t *task) #else fprintf(stderr, "msg: %s\n", msg); #endif + ui_companion_driver_notify_refresh(); goto task_finished; } break; diff --git a/ui/drivers/cocoa/ui_cocoa_application.m b/ui/drivers/cocoa/ui_cocoa_application.m index 1feffc6c95..14975b3c10 100644 --- a/ui/drivers/cocoa/ui_cocoa_application.m +++ b/ui/drivers/cocoa/ui_cocoa_application.m @@ -23,9 +23,9 @@ #include "cocoa_common.h" #include "../../ui_companion_driver.h" -static bool ui_application_cocoa_initialize(void) +static void* ui_application_cocoa_initialize(void) { - return true; + return NULL; } static bool ui_application_cocoa_pending_events(void) @@ -49,9 +49,16 @@ static void ui_application_cocoa_process_events(void) } } -const ui_application_t ui_application_cocoa = { +static void ui_application_cocoa_run(void *args) +{ + (void)args; +} + +ui_application_t ui_application_cocoa = { ui_application_cocoa_initialize, ui_application_cocoa_pending_events, ui_application_cocoa_process_events, + ui_application_cocoa_run, + NULL, "cocoa" }; diff --git a/ui/drivers/cocoa/ui_cocoa_browser_window.m b/ui/drivers/cocoa/ui_cocoa_browser_window.m index c688a62b65..af96902c1c 100644 --- a/ui/drivers/cocoa/ui_cocoa_browser_window.m +++ b/ui/drivers/cocoa/ui_cocoa_browser_window.m @@ -60,7 +60,7 @@ static bool ui_browser_window_cocoa_save(ui_browser_window_state_t *state) return false; } -const ui_browser_window_t ui_browser_window_cocoa = { +ui_browser_window_t ui_browser_window_cocoa = { ui_browser_window_cocoa_open, ui_browser_window_cocoa_save, "cocoa" diff --git a/ui/drivers/cocoa/ui_cocoa_msg_window.m b/ui/drivers/cocoa/ui_cocoa_msg_window.m index 3a9892c79f..d5e4ec9713 100644 --- a/ui/drivers/cocoa/ui_cocoa_msg_window.m +++ b/ui/drivers/cocoa/ui_cocoa_msg_window.m @@ -129,7 +129,7 @@ static enum ui_msg_window_response ui_msg_window_cocoa_warning(ui_msg_window_sta return ui_msg_window_cocoa_dialog(state, UI_MSG_WINDOW_TYPE_WARNING); } -const ui_msg_window_t ui_msg_window_cocoa = { +ui_msg_window_t ui_msg_window_cocoa = { ui_msg_window_cocoa_error, ui_msg_window_cocoa_information, ui_msg_window_cocoa_question, diff --git a/ui/drivers/cocoa/ui_cocoa_window.m b/ui/drivers/cocoa/ui_cocoa_window.m index 881f60b657..3ec869e569 100644 --- a/ui/drivers/cocoa/ui_cocoa_window.m +++ b/ui/drivers/cocoa/ui_cocoa_window.m @@ -25,6 +25,11 @@ #include "../ui_cocoa.h" #include "../../ui_companion_driver.h" +static void* ui_window_cocoa_init(void) +{ + return NULL; +} + static void ui_window_cocoa_destroy(void *data) { ui_window_cocoa_t *cocoa = (ui_window_cocoa_t*)data; @@ -82,7 +87,8 @@ static bool ui_window_cocoa_focused(void *data) return false; } -const ui_window_t ui_window_cocoa = { +ui_window_t ui_window_cocoa = { + ui_window_cocoa_init, ui_window_cocoa_destroy, ui_window_cocoa_set_focused, ui_window_cocoa_set_visible, diff --git a/ui/drivers/null/ui_null_application.c b/ui/drivers/null/ui_null_application.c index ef2394f85e..0c3fea4d91 100644 --- a/ui/drivers/null/ui_null_application.c +++ b/ui/drivers/null/ui_null_application.c @@ -21,9 +21,9 @@ #include "../../ui_companion_driver.h" -static bool ui_application_null_initialize(void) +static void* ui_application_null_initialize(void) { - return true; + return NULL; } static bool ui_application_null_pending_events(void) @@ -35,9 +35,16 @@ static void ui_application_null_process_events(void) { } -const ui_application_t ui_application_null = { +static void ui_application_null_run(void *args) +{ + (void)args; +} + +ui_application_t ui_application_null = { ui_application_null_initialize, ui_application_null_pending_events, ui_application_null_process_events, + ui_application_null_run, + NULL, "null" }; diff --git a/ui/drivers/null/ui_null_browser_window.c b/ui/drivers/null/ui_null_browser_window.c index ab739a3c5f..548069f2f7 100644 --- a/ui/drivers/null/ui_null_browser_window.c +++ b/ui/drivers/null/ui_null_browser_window.c @@ -31,7 +31,7 @@ static bool ui_browser_window_null_save(ui_browser_window_state_t *state) return false; } -const ui_browser_window_t ui_browser_window_null = { +ui_browser_window_t ui_browser_window_null = { ui_browser_window_null_open, ui_browser_window_null_save, "null" diff --git a/ui/drivers/null/ui_null_msg_window.c b/ui/drivers/null/ui_null_msg_window.c index c4de4b7175..adc84ce467 100644 --- a/ui/drivers/null/ui_null_msg_window.c +++ b/ui/drivers/null/ui_null_msg_window.c @@ -41,7 +41,7 @@ static enum ui_msg_window_response ui_msg_window_null_warning(ui_msg_window_stat return UI_MSG_RESPONSE_CANCEL; } -const ui_msg_window_t ui_msg_window_null = { +ui_msg_window_t ui_msg_window_null = { ui_msg_window_null_error, ui_msg_window_null_information, ui_msg_window_null_question, diff --git a/ui/drivers/null/ui_null_window.c b/ui/drivers/null/ui_null_window.c index 4abdf028cb..793019803d 100644 --- a/ui/drivers/null/ui_null_window.c +++ b/ui/drivers/null/ui_null_window.c @@ -21,6 +21,11 @@ #include "../../ui_companion_driver.h" +static void* ui_window_null_init(void) +{ + return NULL; +} + static void ui_window_null_destroy(void *data) { } @@ -47,7 +52,8 @@ static bool ui_window_null_focused(void *data) return true; } -const ui_window_t ui_window_null = { +ui_window_t ui_window_null = { + ui_window_null_init, ui_window_null_destroy, ui_window_null_set_focused, ui_window_null_set_visible, diff --git a/ui/drivers/qt/ui_qt_application.cpp b/ui/drivers/qt/ui_qt_application.cpp index c6c0d42c19..aac3b237c5 100644 --- a/ui/drivers/qt/ui_qt_application.cpp +++ b/ui/drivers/qt/ui_qt_application.cpp @@ -1,5 +1,6 @@ /* RetroArch - A frontend for libretro. * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2018 - Brad Parker * * RetroArch 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 Found- @@ -13,20 +14,130 @@ * If not, see . */ -#include -#include -#include -#include -#include - -#include -#include +#include +extern "C" { #include "../../ui_companion_driver.h" +#include "../../../retroarch.h" +#include "../../../verbosity.h" +#include "../../../version.h" +#include "../../../frontend/frontend.h" +#include "../../../tasks/tasks_internal.h" +#include +} -static bool ui_application_qt_initialize(void) +#include "../ui_qt.h" + +static AppHandler *appHandler; +static ui_application_qt_t ui_application; +static bool app_exiting = false; + +/* these must last for the lifetime of the QApplication */ +static int app_argc = 1; +static char app_name[] = "retroarch"; +static char *app_argv[] = { app_name, NULL }; + +static const unsigned retroarch_qt_icon_data[] = { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, + 0xffffffff, 0xffffffff, 0x000000ff, 0xffffffff, 0xffffffff, 0x00000000, + 0xffffffff, 0xffffffff, 0x000000ff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0x000000ff, 0xffffffff, + 0xffffffff, 0x000000ff, 0xffffffff, 0xffffffff, 0xffffffff, 0x000000ff, + 0xffffffff, 0xffffffff, 0x000000ff, 0xffffffff, 0x00000000, 0x00000000, + 0x00000000, 0xffffffff, 0x000000ff, 0xffffffff, 0x000000ff, 0x000000ff, + 0x000000ff, 0x000000ff, 0x000000ff, 0x000000ff, 0x000000ff, 0xffffffff, + 0x000000ff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, + 0x000000ff, 0x000000ff, 0x000000ff, 0xffffffff, 0x000000ff, 0x000000ff, + 0x000000ff, 0xffffffff, 0x000000ff, 0x000000ff, 0x000000ff, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0x000000ff, 0x000000ff, + 0x000000ff, 0x000000ff, 0x000000ff, 0x000000ff, 0x000000ff, 0x000000ff, + 0x000000ff, 0x000000ff, 0x000000ff, 0xffffffff, 0x00000000, 0x00000000, + 0x00000000, 0xffffffff, 0xffffffff, 0x000000ff, 0x000000ff, 0x000000ff, + 0x000000ff, 0x000000ff, 0x000000ff, 0x000000ff, 0x000000ff, 0x000000ff, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x000000ff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x000000ff, 0xffffffff, 0xffffffff, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0x000000ff, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, + 0xffffffff, 0x000000ff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000 +}; + +AppHandler::AppHandler(QObject *parent) : + QObject(parent) { - return true; +} + +AppHandler::~AppHandler() +{ +} + +void AppHandler::exit() +{ + app_exiting = true; + qApp->closeAllWindows(); +} + +bool AppHandler::isExiting() const +{ + return app_exiting; +} + +void AppHandler::onLastWindowClosed() +{ +} + +static void* ui_application_qt_initialize(void) +{ + appHandler = new AppHandler(); + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) + /* HiDpi supported since Qt 5.6 */ + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); +#endif + + QApplication::setStyle("fusion"); + + ui_application.app = new QApplication(app_argc, app_argv); + ui_application.app->setOrganizationName("libretro"); + ui_application.app->setApplicationName("RetroArch"); + ui_application.app->setApplicationVersion(PACKAGE_VERSION); + ui_application.app->connect(ui_application.app, SIGNAL(lastWindowClosed()), appHandler, SLOT(onLastWindowClosed())); + + { + /* Can't declare the pixmap at the top, because: "QPixmap: Must construct a QGuiApplication before a QPixmap" */ + QImage iconImage(16, 16, QImage::Format_ARGB32); + QPixmap iconPixmap; + unsigned char *bits = iconImage.bits(); + + memcpy(bits, retroarch_qt_icon_data, 16 * 16 * sizeof(unsigned)); + + iconPixmap = QPixmap::fromImage(iconImage); + + ui_application.app->setWindowIcon(QIcon(iconPixmap)); + } + + return &ui_application; } static bool ui_application_qt_pending_events(void) @@ -36,13 +147,58 @@ static bool ui_application_qt_pending_events(void) static void ui_application_qt_process_events(void) { - while(ui_application_qt_pending_events()) + if(ui_application_qt_pending_events()) QApplication::processEvents(); } -const ui_application_t ui_application_qt = { +static void ui_application_qt_quit(void) +{ + appHandler->exit(); +} + +static void ui_application_qt_run(void *args) +{ +#ifdef HAVE_MAIN + int ret; + unsigned sleep_ms = 0; + + do + { + ui_application_qt_process_events(); + + ret = runloop_iterate(&sleep_ms); + + if (ret == 1 && sleep_ms > 0) + retro_sleep(sleep_ms); + + task_queue_check(); + + if (ret == -1 || app_exiting) + { + ui_application_qt_quit(); + break; + } + }while(1); + + main_exit(args); +#endif +} + +#ifdef HAVE_MAIN +#ifdef __cplusplus +extern "C" +#endif +int main(int argc, char *argv[]) +{ + return rarch_main(argc, argv, NULL); +} +#endif + +ui_application_t ui_application_qt = { ui_application_qt_initialize, ui_application_qt_pending_events, ui_application_qt_process_events, + ui_application_qt_run, + ui_application_qt_quit, "qt" }; diff --git a/ui/drivers/qt/ui_qt_browser_window.cpp b/ui/drivers/qt/ui_qt_browser_window.cpp index 7272b07701..cd7fa8034a 100644 --- a/ui/drivers/qt/ui_qt_browser_window.cpp +++ b/ui/drivers/qt/ui_qt_browser_window.cpp @@ -1,5 +1,6 @@ /* RetroArch - A frontend for libretro. * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2018 - Brad Parker * * RetroArch 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 Found- @@ -34,7 +35,7 @@ static bool ui_browser_window_qt_save(ui_browser_window_state_t *state) return false; } -const ui_browser_window_t ui_browser_window_qt = { +ui_browser_window_t ui_browser_window_qt = { ui_browser_window_qt_open, ui_browser_window_qt_save, "qt" diff --git a/ui/drivers/qt/ui_qt_load_core_window.cpp b/ui/drivers/qt/ui_qt_load_core_window.cpp new file mode 100644 index 0000000000..65da0381fc --- /dev/null +++ b/ui/drivers/qt/ui_qt_load_core_window.cpp @@ -0,0 +1,283 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2018 - Brad Parker + * + * RetroArch 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 Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch 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 RetroArch. + * If not, see . + */ + +#include "ui_qt_load_core_window.h" + +#include +#include + +extern "C" { +#include "../../../core_info.h" +#include "../../../verbosity.h" +#include "../../../configuration.h" +#include "../../../msg_hash.h" +#include "../../../retroarch.h" +#include "../../../command.h" +#include "../../../frontend/frontend_driver.h" +#include +#include +#include +}; + +#define CORE_NAME_COLUMN 0 +#define CORE_VERSION_COLUMN 1 + +LoadCoreTableWidget::LoadCoreTableWidget(QWidget *parent) : + QTableWidget(parent) +{ +} + +void LoadCoreTableWidget::keyPressEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) + { + event->accept(); + emit enterPressed(); + } + else + QTableWidget::keyPressEvent(event); +} + +LoadCoreWindow::LoadCoreWindow(QWidget *parent) : + QMainWindow(parent) + ,m_layout() + ,m_table(new LoadCoreTableWidget()) + ,m_statusLabel(new QLabel()) +{ + QHBoxLayout *hbox = new QHBoxLayout(); + QPushButton *customCoreButton = new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_LOAD_CUSTOM_CORE)); + + connect(customCoreButton, SIGNAL(clicked()), this, SLOT(onLoadCustomCoreClicked())); + connect(m_table, SIGNAL(enterPressed()), this, SLOT(onCoreEnterPressed())); + connect(m_table, SIGNAL(cellDoubleClicked(int,int)), this, SLOT(onCellDoubleClicked(int,int))); + + setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_LOAD_CORE)); + + setCentralWidget(new QWidget()); + + centralWidget()->setLayout(&m_layout); + + hbox->addWidget(customCoreButton); + hbox->addItem(new QSpacerItem(width(), 20, QSizePolicy::Expanding, QSizePolicy::Minimum)); + + m_layout.addWidget(m_table); + m_layout.addLayout(hbox); + + statusBar()->addPermanentWidget(m_statusLabel); +} + +void LoadCoreWindow::closeEvent(QCloseEvent *event) +{ + emit windowClosed(); + + QWidget::closeEvent(event); +} + +void LoadCoreWindow::keyPressEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Escape) + { + event->accept(); + close(); + } + else + QMainWindow::keyPressEvent(event); +} + +void LoadCoreWindow::setStatusLabel(QString label) +{ + m_statusLabel->setText(label); +} + +void LoadCoreWindow::onCellDoubleClicked(int, int) +{ + onCoreEnterPressed(); +} + +void LoadCoreWindow::loadCore(const char *path) +{ + QProgressDialog progress(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_LOADING_CORE), QString(), 0, 0, this); + progress.setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_LOAD_CORE)); + progress.setMinimumDuration(0); + progress.setValue(progress.minimum()); + progress.show(); + + /* Because core loading will block, we need to go ahead and process pending events that would allow the progress dialog to fully show its contents before actually starting the core loading process. Must call processEvents() twice. */ + qApp->processEvents(); + qApp->processEvents(); + +#ifdef HAVE_DYNAMIC + /* const-removing cast is safe here because the path is never written to */ + rarch_ctl(RARCH_CTL_SET_LIBRETRO_PATH, const_cast(path)); + + if (!command_event(CMD_EVENT_LOAD_CORE, NULL)) + { + QMessageBox::critical(this, msg_hash_to_str(MSG_ERROR), msg_hash_to_str(MSG_FAILED_TO_OPEN_LIBRETRO_CORE)); + return; + } + + setProperty("last_launch_with_index", -1); + + emit coreLoaded(); +#endif +} + +void LoadCoreWindow::onCoreEnterPressed() +{ + QTableWidgetItem *selectedCoreItem = NULL; + QString path; + QByteArray pathArray; + const char *pathData = NULL; + QVariantHash hash; + + selectedCoreItem = m_table->item(m_table->currentRow(), CORE_NAME_COLUMN); + hash = selectedCoreItem->data(Qt::UserRole).toHash(); + path = hash["path"].toString(); + + pathArray.append(path); + pathData = pathArray.constData(); + + loadCore(pathData); +} + +void LoadCoreWindow::onLoadCustomCoreClicked() +{ + QString path; + QByteArray pathArray; + settings_t *settings = config_get_ptr(); + char core_ext[255] = {0}; + char filters[PATH_MAX_LENGTH] = {0}; + const char *pathData = NULL; + + frontend_driver_get_core_extension(core_ext, sizeof(core_ext)); + + strlcpy(filters, "Cores (*.", sizeof(filters)); + strlcat(filters, core_ext, sizeof(filters)); + strlcat(filters, ");;All Files (*.*)", sizeof(filters)); + + path = QFileDialog::getOpenFileName(this, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_LOAD_CORE), settings->paths.directory_libretro, filters, NULL); + + if (path.isEmpty()) + return; + + pathArray.append(path); + pathData = pathArray.constData(); + + loadCore(pathData); +} + +void LoadCoreWindow::initCoreList(const QStringList &extensionFilters) +{ + core_info_list_t *cores = NULL; + QStringList horizontal_header_labels; + QDesktopWidget *desktop = qApp->desktop(); + QRect desktopRect = desktop->availableGeometry(); + unsigned i = 0; + int j = 0; + + horizontal_header_labels << msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_NAME); + horizontal_header_labels << msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CORE_VERSION); + + core_info_get_list(&cores); + + m_table->clear(); + m_table->setColumnCount(0); + m_table->setRowCount(0); + m_table->setSelectionBehavior(QAbstractItemView::SelectRows); + m_table->setSelectionMode(QAbstractItemView::SingleSelection); + m_table->setSortingEnabled(false); + m_table->setRowCount(cores->count); + m_table->setColumnCount(2); + m_table->setHorizontalHeaderLabels(horizontal_header_labels); + + for (i = 0; i < cores->count; i++) + { + core_info_t *core = core_info_get(cores, i); + QTableWidgetItem *name_item = NULL; + QTableWidgetItem *version_item = new QTableWidgetItem(core->display_version); + QVariantHash hash; + const char *name = core->display_name; + + if (string_is_empty(name)) + name = path_basename(core->path); + + name_item = new QTableWidgetItem(name); + + hash["path"] = core->path; + hash["extensions"] = QString(core->supported_extensions).split("|"); + + name_item->setData(Qt::UserRole, hash); + name_item->setFlags(name_item->flags() & ~Qt::ItemIsEditable); + version_item->setFlags(version_item->flags() & ~Qt::ItemIsEditable); + + m_table->setItem(i, CORE_NAME_COLUMN, name_item); + m_table->setItem(i, CORE_VERSION_COLUMN, version_item); + } + + if (!extensionFilters.isEmpty()) + { + QVector rowsToHide; + + for (j = 0; j < m_table->rowCount(); j++) + { + bool found = false; + QTableWidgetItem *item = m_table->item(j, CORE_NAME_COLUMN); + QVariantHash hash; + QStringList extensions; + int k = 0; + + if (!item) + continue; + + hash = item->data(Qt::UserRole).toHash(); + extensions = hash["extensions"].toStringList(); + + if (!extensions.isEmpty()) + { + for (k = 0; k < extensions.size(); k++) + { + QString ext = extensions.at(k).toLower(); + + if (extensionFilters.contains(ext, Qt::CaseInsensitive)) + { + found = true; + break; + } + } + + if (!found) + rowsToHide.append(j); + } + } + + if (rowsToHide.size() != m_table->rowCount()) + { + foreach (const int &row, rowsToHide) + { + m_table->setRowHidden(row, true); + } + } + } + + m_table->setSortingEnabled(true); + m_table->resizeColumnsToContents(); + m_table->sortByColumn(0, Qt::AscendingOrder); + m_table->selectRow(0); + m_table->setAlternatingRowColors(true); + + resize(qMin(desktopRect.width(), contentsMargins().left() + m_table->horizontalHeader()->length() + contentsMargins().right()), height()); +} diff --git a/ui/drivers/qt/ui_qt_load_core_window.h b/ui/drivers/qt/ui_qt_load_core_window.h new file mode 100644 index 0000000000..924af4f2cf --- /dev/null +++ b/ui/drivers/qt/ui_qt_load_core_window.h @@ -0,0 +1,67 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2018 - Brad Parker + * + * RetroArch 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 Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch 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 RetroArch. + * If not, see . + */ + +#ifndef _QT_LOAD_CORE_WINDOW_H +#define _QT_LOAD_CORE_WINDOW_H + +extern "C" { +#include +} + +#include + +class LoadCoreTableWidget : public QTableWidget +{ + Q_OBJECT +public: + LoadCoreTableWidget(QWidget *parent = NULL); +signals: + void enterPressed(); +protected: + void keyPressEvent(QKeyEvent *event); +}; + +class LoadCoreWindow : public QMainWindow +{ + Q_OBJECT +public: + LoadCoreWindow(QWidget *parent = 0); + void initCoreList(const QStringList &extensionFilters = QStringList()); + void setStatusLabel(QString label); +signals: + void coreLoaded(); + void windowClosed(); +private slots: + void onLoadCustomCoreClicked(); + void onCoreEnterPressed(); + void onCellDoubleClicked(int row, int column); +protected: + void keyPressEvent(QKeyEvent *event); + void closeEvent(QCloseEvent *event); +private: + void loadCore(const char *path); + + QVBoxLayout m_layout; + LoadCoreTableWidget *m_table; + QLabel *m_statusLabel; +}; + +RETRO_BEGIN_DECLS + +RETRO_END_DECLS + +#endif diff --git a/ui/drivers/qt/ui_qt_msg_window.cpp b/ui/drivers/qt/ui_qt_msg_window.cpp index 3d4a3fb497..280aa68b2e 100644 --- a/ui/drivers/qt/ui_qt_msg_window.cpp +++ b/ui/drivers/qt/ui_qt_msg_window.cpp @@ -1,5 +1,6 @@ /* RetroArch - A frontend for libretro. * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2018 - Brad Parker * * RetroArch 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 Found- @@ -97,7 +98,7 @@ static enum ui_msg_window_response ui_msg_window_qt_warning(ui_msg_window_state return ui_msg_window_qt_response(state, QMessageBox::warning((QWidget*)state->window, state->title, state->text, flags)); } -const ui_msg_window_t ui_msg_window_qt = { +ui_msg_window_t ui_msg_window_qt = { ui_msg_window_qt_error, ui_msg_window_qt_information, ui_msg_window_qt_question, diff --git a/ui/drivers/qt/ui_qt_themes.h b/ui/drivers/qt/ui_qt_themes.h new file mode 100644 index 0000000000..058a073bc7 --- /dev/null +++ b/ui/drivers/qt/ui_qt_themes.h @@ -0,0 +1,356 @@ +#include + +/* %1 is a placeholder for palette(highlight) or the equivalent chosen by the user */ +static const QString qt_theme_default_stylesheet = QStringLiteral(""); + +static const QString qt_theme_dark_stylesheet = QStringLiteral("" + "QWidget {\n" + " color:white;\n" + " background-color:rgb(53,53,53);\n" + " selection-background-color:%1;\n" + "}\n" + "QWidget#playlistWidget, QWidget#browserWidget, QWidget#tableWidget, QWidget#logWidget {\n" + " background-color:rgb(66,66,66);\n" + " border-top:1px solid rgba(175,175,175,50%);\n" + " border-left:1px solid rgba(125,125,125,50%);\n" + " border-right:1px solid rgba(125,125,125,50%);\n" + " border-bottom:1px solid rgba(25,25,25,75%);\n" + "}\n" + "QToolTip {\n" + " color:white;\n" + " background-color:rgb(53,53,53);\n" + " border:1px solid rgb(80,80,80);\n" + " border-radius:4px;\n" + "}\n" + "QMenuBar {\n" + " background-color:qlineargradient(x1:0,y1:0,x2:0,y2:1,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));\n" + " border-bottom:2px solid rgba(25,25,25,75);\n" + "}\n" + "QMenuBar::item {\n" + " spacing:2px;\n" + " padding:3px 4px;\n" + " background-color:transparent;\n" + "}\n" + "QMenuBar::item:selected {\n" + " background-color:qlineargradient(x1:0,y1:0,x2:0,y2:1,stop:0 rgba(106,106,106,255),stop:1 rgba(106,106,106,75));\n" + " border:1px solid %1;\n" + "}\n" + "QMenuBar::item:pressed {\n" + " background-color:%1;\n" + " border-left:1px solid rgba(25,25,25,127);\n" + " border-right:1px solid rgba(25,25,25,127);\n" + "}\n" + "QMenu {\n" + " background-color:rgb(45,45,45);\n" + " border:1px solid palette(shadow);\n" + "}\n" + "QMenu::item {\n" + " padding:3px 25px 3px 25px;\n" + " border:1px solid transparent;\n" + "}\n" + "QMenu::item:disabled {\n" + " color:rgb(127,127,127);\n" + "}\n" + "QMenu::item:selected {\n" + " border-color:rgba(200,200,200,127);\n" + " background-color:%1;\n" + "}\n" + "QMenu::icon:checked {\n" + " background-color:qlineargradient(x1:0,y1:1,x2:0,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));\n" + " border:1px solid %1;\n" + " border-radius:2px;\n" + "}\n" + "QMenu::separator {\n" + " height:1px;\n" + " background-color:rgb(100,100,100);\n" + " margin-left:5px;\n" + " margin-right:5px;\n" + "}\n" + "QMenu::indicator {\n" + " width:18px;\n" + " height:18px;\n" + "}\n" + "QToolBar::top {\n" + " background-color:qlineargradient(x1:0,y1:0,x2:0,y2:1,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));\n" + " border-bottom:3px solid qlineargradient(x1:0,y1:0,x2:0,y2:1,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));\n" + "}\n" + "QToolBar::bottom {\n" + " background-color:qlineargradient(x1:0,y1:1,x2:0,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));\n" + " border-top:3px solid qlineargradient(x1:0,y1:1,x2:0,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));\n" + "}\n" + "QToolBar::left {\n" + " background-color:qlineargradient(x1:0,y1:0,x2:1,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));\n" + " border-right:3px solid qlineargradient(x1:0,y1:0,x2:1,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));\n" + "}\n" + "QToolBar::right {\n" + " background-color:qlineargradient(x1:1,y1:0,x2:0,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));\n" + " border-left:3px solid qlineargradient(x1:1,y1:0,x2:0,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));\n" + "}\n" + "QMainWindow {\n" + " background-color:rgb(53,53,53);\n" + "}\n" + "QMainWindow::separator {\n" + " width:6px;\n" + " height:5px;\n" + " padding:2px;\n" + " background-color:rgba(25,25,25,50%);\n" + "}\n" + "QLineEdit {\n" + " color:white;\n" + " background-color:rgb(25,25,25);\n" + "}\n" + "QLineEdit::focus {\n" + " border:1px solid %1;\n" + " border-radius:3px;\n" + " color:white;\n" + " background-color:rgb(25,25,25);\n" + "}\n" + "QSplitter::handle:horizontal {\n" + " width:10px;\n" + "}\n" + "QSplitter::handle:vertical {\n" + " height:10px;\n" + "}\n" + "QMainWindow::separator:hover, QSplitter::handle:hover {\n" + "}\n" + "QDockWidget::title {\n" + " padding:4px;\n" + " background-color:qlineargradient(x1:0,y1:1,x2:0,y2:0,stop:0 rgba(25,25,25,175),stop:1 rgba(53,53,53,75));\n" + " border:1px solid rgba(25,25,25,75);\n" + " border-top:1px solid rgba(175,175,175,50%);\n" + " border-bottom:1px solid rgba(25,25,25,127);\n" + "}\n" + "QDockWidget::close-button, QDockWidget::float-button {\n" + " subcontrol-position:top right;\n" + " subcontrol-origin:margin;\n" + " position:absolute;\n" + " top:3px;\n" + " bottom:0px;\n" + " width:20px;\n" + " height:20px;\n" + "}\n" + "QDockWidget::close-button:hover, QDockWidget::float-button:hover {\n" + " border:1px solid %1;\n" + " border-radius:4px;\n" + "}\n" + "QDockWidget::close-button {\n" + " right:3px;\n" + "}\n" + "QDockWidget::float-button {\n" + " right:25px;\n" + "}\n" + "QGroupBox {\n" + " background-color:rgba(66,66,66,50%);\n" + " margin-top:27px;\n" + " border:1px solid rgba(25,25,25,127);\n" + " border-top-left-radius:4px;\n" + " border-top-right-radius:4px;\n" + "}\n" + "QGroupBox::title {\n" + " subcontrol-origin:margin;\n" + " subcontrol-position:left top;\n" + " padding:4px 6px;\n" + " margin-left:3px;\n" + " background-color:qlineargradient(x1:0,y1:1,x2:0,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));\n" + " border:1px solid rgba(25,25,25,75);\n" + " border-top-left-radius:4px;\n" + " border-top-right-radius:4px;\n" + "}\n" + "QTabWidget::pane {\n" + " background-color:rgba(66,66,66,50%);\n" + "}\n" + "QTabWidget::tab-bar {\n" + "}\n" + "QTabBar {\n" + " background-color:transparent;\n" + " qproperty-drawBase:0;\n" + " border-bottom:1px solid rgba(25,25,25,50%);\n" + "}\n" + "QTabBar::tab {\n" + " padding:4px 6px;\n" + " background-color:rgba(25,25,25,127);\n" + " border:1px solid rgba(25,25,25,75);\n" + "}\n" + "QTabBar::tab:selected {\n" + " background-color:rgb(66,66,66);\n" + " border-bottom-color:rgba(66,66,66,75%);\n" + "}\n" + "QTabBar::tab:!selected {\n" + " color:rgb(175,175,175);\n" + "}\n" + "QComboBox {\n" + " min-height:20px;\n" + " padding:1px 18px 1px 3px;\n" + "}\n" + "QComboBox::focus {\n" + " background:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 rgba(255,255,255,50), stop: 1 rgba(100,100,100,25));\n" + " border:1px solid %1;\n" + " border-radius:4px;\n" + "}\n" + "QComboBox::hover {\n" + " background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 rgba(255,255,255,50), stop: 1 rgba(127,127,127,50));\n" + " border:1px solid %1;\n" + " border-radius:4px;\n" + "}\n" + "QComboBox::drop-down {\n" + " background-color:transparent;\n" + "}\n" + "QComboBox::selected:on, QComboBox::selected:off {\n" + " background-color:%1;\n" + "}\n" + "QTabBar::tab:hover {\n" + " color:white;\n" + " background-color:%1;\n" + "}\n" + "QComboBox::separator {\n" + " background-color:rgb(100,100,100);\n" + " height:1px;\n" + " margin-left:4px;\n" + " margin-right:4px;\n" + "}\n" + "QCheckBox::indicator {\n" + " width:18px;\n" + " height:18px;\n" + "}\n" + "QPushButton {\n" + " min-height:20px;\n" + " min-width:80px;\n" + " padding:1px 3px 1px 3px;\n" + "}\n" + "QPushButton::focus, QToolButton::focus {\n" + " background:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 rgba(255,255,255,50), stop: 1 rgba(100,100,100,25));\n" + " border:1px solid %1;\n" + " border-radius:4px;\n" + "}\n" + "QPushButton::hover, QToolButton::hover {\n" + " background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 rgba(255,255,255,75), stop: 1 rgba(100,100,100,50));\n" + " border:1px solid %1;\n" + " border-radius:4px;\n" + "}\n" + "QPushButton::pressed, QToolButton::pressed {\n" + " background-color:transparent;\n" + " border:1px solid %1;\n" + " border-radius:4px;\n" + "}\n" + "QRadioButton::indicator {\n" + " width:18px;\n" + " height:18px;\n" + "}\n" + "QListWidget::item:selected, QTreeView::item:selected, QTableView::item:selected {\n" + " color:white;\n" + " background-color:%1;\n" + "}\n" + "QTreeView {\n" + " background-color:rgb(25,25,25);\n" + " selection-background-color:%1;\n" + "}\n" + "QTreeView::branch:selected {\n" + " background-color:%1;\n" + "}\n" + "QTreeView::item:selected:disabled, QTableView::item:selected:disabled {\n" + " background-color:rgb(80,80,80);\n" + "}\n" + "QTreeView::branch:open, QTreeView::branch:closed {\n" + " background-color:solid;\n" + "}\n" + "QTableView, QListWidget {\n" + " background-color:rgb(25,25,25);\n" + "}\n" + "QTreeView QHeaderView::section, QTableView QHeaderView::section {\n" + " /*height:24px;*/\n" + " background-color:qlineargradient(x1:0,y1:1,x2:0,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));\n" + " border-style:none;\n" + " border-bottom:1px solid rgb(65,65,65);\n" + " padding-left:5px;\n" + " padding-right:5px;\n" + "}\n" + "QTableWidget {\n" + " background-color:rgb(25,25,25);\n" + " alternate-background-color:rgb(40,40,40);\n" + "}\n" + "QScrollBar:vertical, QScrollBar:horizontal {\n" + " background-color:rgb(35,35,35);\n" + "}\n" + "QScrollBar::handle:vertical, QScrollBar::handle:horizontal {\n" + " background-color:rgb(65,65,65);\n" + " border-right:1px solid rgba(175,175,175,50%);\n" + " border-top:1px solid rgba(175,175,175,50%);\n" + " border-bottom:1px solid rgba(25,25,25,75);\n" + " border-radius:2px;\n" + "}\n" + "QScrollBar::handle:horizontal:hover, QScrollBar::handle:vertical:hover {\n" + " border:1px solid %1;\n" + " background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 rgba(255,255,255,75), stop: 1 rgba(127,127,127,75));\n" + "}\n" + "QScrollBar:vertical {\n" + " border-top-right-radius:2px;\n" + " border-bottom-right-radius:2px;\n" + " width:16px;\n" + " margin:0px;\n" + "}\n" + "QScrollBar::handle:vertical {\n" + " min-height:20px;\n" + " margin:2px 4px 2px 4px;\n" + "}\n" + "QScrollBar::add-line:vertical {\n" + " background:none;\n" + " height:0px;\n" + " subcontrol-position:right;\n" + " subcontrol-origin:margin;\n" + "}\n" + "QScrollBar::sub-line:vertical {\n" + " background:none;\n" + " height:0px;\n" + " subcontrol-position:left;\n" + " subcontrol-origin:margin;\n" + "}\n" + "QScrollBar:horizontal {\n" + " height:16px;\n" + " margin:0px;\n" + "}\n" + "QScrollBar::handle:horizontal {\n" + " min-width:20px;\n" + " margin:4px 2px 4px 2px;\n" + "}\n" + "QScrollBar::add-line:horizontal {\n" + " background:none;\n" + " width:0px;\n" + " subcontrol-position:bottom;\n" + " subcontrol-origin:margin;\n" + "}\n" + "QScrollBar::sub-line:horizontal {\n" + " background:none;\n" + " width:0px;\n" + " subcontrol-position:top;\n" + " subcontrol-origin:margin;\n" + "}\n" + "QSlider::handle:horizontal {\n" + " border-radius:4px;\n" + " border:1px solid rgba(25,25,25,255);\n" + " background-color:palette(alternate-base);\n" + " min-height:20px;\n" + " margin:0 -4px;\n" + "}\n" + "QSlider::handle:horizontal:hover {\n" + " background-color:%1;\n" + "}\n" + "QSlider::add-page:horizontal {\n" + " background-color:palette(base);\n" + "}\n" + "QSlider::sub-page:horizontal {\n" + " background-color:%1;\n" + "}\n" + "QSlider::sub-page:horizontal:disabled {\n" + " background-color:rgb(80,80,80);\n" + "}\n" + "QStatusBar {\n" + " color:white;\n" + " background-color:qlineargradient(x1:0,y1:0,x2:0,y2:1,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));\n" + "}\n" + "QStatusBar QLabel {\n" + " background-color:transparent;\n" + "}\n" + "QSizeGrip {\n" + " background-color:solid;\n" + "}\n" +); diff --git a/ui/drivers/qt/ui_qt_window.cpp b/ui/drivers/qt/ui_qt_window.cpp index aca4448c22..e3643a490f 100644 --- a/ui/drivers/qt/ui_qt_window.cpp +++ b/ui/drivers/qt/ui_qt_window.cpp @@ -1,5 +1,6 @@ /* RetroArch - A frontend for libretro. * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2018 - Brad Parker * * RetroArch 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 Found- @@ -13,59 +14,2887 @@ * If not, see . */ -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -#include "wrapper/wrapper.h" #include "../ui_qt.h" +#include "ui_qt_load_core_window.h" +#include "ui_qt_themes.h" -#include "../../ui_companion_driver.h" +extern "C" { +#include "../../../version.h" +#include "../../../verbosity.h" +#include "../../../retroarch.h" +#include "../../../msg_hash.h" +#include "../../../core_info.h" +#include "../../../content.h" +#include "../../../paths.h" +#include "../../../configuration.h" +#include "../../../file_path_special.h" +#include "../../../playlist.h" +#include "../../../content.h" +#include "../../../menu/menu_driver.h" +#include "../../../tasks/tasks_internal.h" +#include +#include +#include +#include +} + +#define TIMER_MSEC 1000 /* periodic timer for gathering statistics */ + +#ifndef COLLECTION_SIZE +#define COLLECTION_SIZE 99999 +#endif + +#define GENERIC_FOLDER_ICON "/xmb/dot-art/png/folder.png" +#define ICON_PATH "/xmb/dot-art/png/" +#define THUMBNAIL_BOXART "Named_Boxarts" +#define THUMBNAIL_SCREENSHOT "Named_Snaps" +#define THUMBNAIL_TITLE "Named_Titles" +#define ALL_PLAYLISTS_TOKEN "|||ALL|||" +#define HIRAGANA_START 0x3041U +#define HIRAGANA_END 0x3096U +#define KATAKANA_START 0x30A1U +#define KATAKANA_END 0x30F6U +#define HIRA_KATA_OFFSET (KATAKANA_START - HIRAGANA_START) + +static ui_window_qt_t ui_window = {0}; + +enum CoreSelection +{ + CORE_SELECTION_CURRENT, + CORE_SELECTION_PLAYLIST_SAVED, + CORE_SELECTION_PLAYLIST_DEFAULT, + CORE_SELECTION_ASK, + CORE_SELECTION_LOAD_CORE +}; + +#ifdef HAVE_LIBRETRODB +static void scan_finished_handler(void *task_data, void *user_data, const char *err) +{ + menu_ctx_environment_t menu_environ; + menu_environ.type = MENU_ENVIRON_RESET_HORIZONTAL_LIST; + menu_environ.data = NULL; + + menu_driver_ctl(RARCH_MENU_CTL_ENVIRONMENT, &menu_environ); + + if (!ui_window.qtWindow->settings()->value("scan_finish_confirm", true).toBool()) + return; + + if (!ui_window.qtWindow->showMessageBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_SCAN_FINISHED), MainWindow::MSGBOX_TYPE_INFO, Qt::ApplicationModal)) + ui_window.qtWindow->settings()->setValue("scan_finish_confirm", false); +} +#endif + +TreeView::TreeView(QWidget *parent) : + QTreeView(parent) +{ +} + +void TreeView::columnCountChanged(int oldCount, int newCount) +{ + QTreeView::columnCountChanged(oldCount, newCount); +} + +void TreeView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) +{ + QModelIndexList list = selected.indexes(); + + QTreeView::selectionChanged(selected, deselected); + + emit itemsSelected(list); +} + +TableWidget::TableWidget(QWidget *parent) : + QTableWidget(parent) +{ +} + +void TableWidget::keyPressEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) + { + event->accept(); + emit enterPressed(); + } + else + QTableWidget::keyPressEvent(event); +} + +CoreInfoLabel::CoreInfoLabel(QString text, QWidget *parent) : + QLabel(text, parent) +{ + setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); +} + +CoreInfoDialog::CoreInfoDialog(MainWindow *mainwindow, QWidget *parent) : + QDialog(parent) + ,m_formLayout(new QFormLayout()) + ,m_mainwindow(mainwindow) +{ + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok); + + connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + + setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFORMATION)); + + m_formLayout->setFormAlignment(Qt::AlignCenter); + m_formLayout->setLabelAlignment(Qt::AlignCenter); + + setLayout(new QVBoxLayout()); + + qobject_cast(layout())->addLayout(m_formLayout); + layout()->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding)); + layout()->addWidget(buttonBox); +} + +void CoreInfoDialog::showCoreInfo() +{ + int row = 0; + int rowCount = m_formLayout->rowCount(); + int i = 0; + QList > infoList = m_mainwindow->getCoreInfo(); + + if (rowCount > 0) + { + for (row = 0; row < rowCount; row++) + { +#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)) + /* removeRow() and takeRow() was only added in 5.8! */ + m_formLayout->removeRow(0); +#else + /* something is buggy here... sometimes items appear duplicated, and other times not */ + QLayoutItem *item = m_formLayout->itemAt(0); + QWidget *w = NULL; + + if (item) + { + w = item->widget(); + + if (w) + { + QWidget *label = m_formLayout->labelForField(w); + + if (label) + delete label; + + m_formLayout->removeWidget(w); + + delete w; + } + } +#endif + } + } + + if (infoList.count() == 0) + return; + + for (i = 0; i < infoList.count(); i++) + { + const QHash &line = infoList.at(i); + QLabel *label = new QLabel(line.value("key")); + CoreInfoLabel *value = new CoreInfoLabel(line.value("value")); + QString labelStyle = line.value("label_style"); + QString valueStyle = line.value("value_style"); + + if (!labelStyle.isEmpty()) + label->setStyleSheet(labelStyle); + + if (!valueStyle.isEmpty()) + value->setStyleSheet(valueStyle); + + m_formLayout->addRow(label, value); + } + + show(); +} + +ViewOptionsDialog::ViewOptionsDialog(MainWindow *mainwindow, QWidget *parent) : + QDialog(parent) + ,m_mainwindow(mainwindow) + ,m_settings(mainwindow->settings()) + ,m_saveGeometryCheckBox(new QCheckBox(this)) + ,m_saveDockPositionsCheckBox(new QCheckBox(this)) + ,m_saveLastTabCheckBox(new QCheckBox(this)) + ,m_showHiddenFilesCheckBox(new QCheckBox(this)) + ,m_themeComboBox(new QComboBox(this)) + ,m_highlightColorPushButton(new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CHOOSE), this)) + ,m_highlightColor() + ,m_highlightColorLabel(new QLabel(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_HIGHLIGHT_COLOR), this)) + ,m_customThemePath() +{ + QFormLayout *form = new QFormLayout(); + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + + setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_TITLE)); + + m_themeComboBox->addItem(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THEME_SYSTEM_DEFAULT), MainWindow::THEME_SYSTEM_DEFAULT); + m_themeComboBox->addItem(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THEME_DARK), MainWindow::THEME_DARK); + m_themeComboBox->addItem(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THEME_CUSTOM), MainWindow::THEME_CUSTOM); + + form->setFormAlignment(Qt::AlignCenter); + form->setLabelAlignment(Qt::AlignCenter); + + setLayout(new QVBoxLayout(this)); + + connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + + connect(this, SIGNAL(accepted()), this, SLOT(onAccepted())); + connect(this, SIGNAL(rejected()), this, SLOT(onRejected())); + + form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_SAVE_GEOMETRY), m_saveGeometryCheckBox); + form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_SAVE_DOCK_POSITIONS), m_saveDockPositionsCheckBox); + form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_SAVE_LAST_TAB), m_saveLastTabCheckBox); + form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SHOW_HIDDEN_FILES), m_showHiddenFilesCheckBox); + form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THEME), m_themeComboBox); + form->addRow(m_highlightColorLabel, m_highlightColorPushButton); + + qobject_cast(layout())->addLayout(form); + layout()->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding)); + layout()->addWidget(buttonBox); + + loadViewOptions(); + + connect(m_themeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onThemeComboBoxIndexChanged(int))); + connect(m_highlightColorPushButton, SIGNAL(clicked()), this, SLOT(onHighlightColorChoose())); +} + +void ViewOptionsDialog::onThemeComboBoxIndexChanged(int) +{ + MainWindow::Theme theme = static_cast(m_themeComboBox->currentData(Qt::UserRole).toInt()); + + if (theme == MainWindow::THEME_CUSTOM) + { + QString filePath = QFileDialog::getOpenFileName(this, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_SELECT_THEME)); + + if (filePath.isEmpty()) + { + int oldThemeIndex = m_themeComboBox->findData(m_mainwindow->getThemeFromString(m_settings->value("theme", "default").toString())); + + if (m_themeComboBox->count() > oldThemeIndex) + { + disconnect(m_themeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onThemeComboBoxIndexChanged(int))); + m_themeComboBox->setCurrentIndex(oldThemeIndex); + connect(m_themeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onThemeComboBoxIndexChanged(int))); + } + } + else + { + m_customThemePath = filePath; + + if (m_mainwindow->setCustomThemeFile(filePath)) + m_mainwindow->setTheme(theme); + } + } + else + m_mainwindow->setTheme(theme); + + showOrHideHighlightColor(); +} + +void ViewOptionsDialog::onHighlightColorChoose() +{ + QPixmap highlightPixmap(m_highlightColorPushButton->iconSize()); + QColor currentHighlightColor = m_settings->value("highlight_color", QApplication::palette().highlight().color()).value(); + QColor newHighlightColor = QColorDialog::getColor(currentHighlightColor, this, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_SELECT_COLOR)); + + if (newHighlightColor.isValid()) + { + MainWindow::Theme theme = static_cast(m_themeComboBox->currentData(Qt::UserRole).toInt()); + + m_highlightColor = newHighlightColor; + m_settings->setValue("highlight_color", m_highlightColor); + highlightPixmap.fill(m_highlightColor); + m_highlightColorPushButton->setIcon(highlightPixmap); + m_mainwindow->setTheme(theme); + } +} + +void ViewOptionsDialog::loadViewOptions() +{ + QColor highlightColor = m_settings->value("highlight_color", QApplication::palette().highlight().color()).value(); + QPixmap highlightPixmap(m_highlightColorPushButton->iconSize()); + int themeIndex = 0; + + m_saveGeometryCheckBox->setChecked(m_settings->value("save_geometry", false).toBool()); + m_saveDockPositionsCheckBox->setChecked(m_settings->value("save_dock_positions", false).toBool()); + m_saveLastTabCheckBox->setChecked(m_settings->value("save_last_tab", false).toBool()); + m_showHiddenFilesCheckBox->setChecked(m_settings->value("show_hidden_files", true).toBool()); + + themeIndex = m_themeComboBox->findData(m_mainwindow->getThemeFromString(m_settings->value("theme", "default").toString())); + + if (m_themeComboBox->count() > themeIndex) + m_themeComboBox->setCurrentIndex(themeIndex); + + if (highlightColor.isValid()) + { + m_highlightColor = highlightColor; + highlightPixmap.fill(m_highlightColor); + m_highlightColorPushButton->setIcon(highlightPixmap); + } + + showOrHideHighlightColor(); +} + +void ViewOptionsDialog::showOrHideHighlightColor() +{ + if (m_mainwindow->theme() == MainWindow::THEME_DARK) + { + m_highlightColorLabel->show(); + m_highlightColorPushButton->show(); + } + else + { + m_highlightColorLabel->hide(); + m_highlightColorPushButton->hide(); + } +} + +void ViewOptionsDialog::saveViewOptions() +{ + m_settings->setValue("save_geometry", m_saveGeometryCheckBox->isChecked()); + m_settings->setValue("save_dock_positions", m_saveDockPositionsCheckBox->isChecked()); + m_settings->setValue("save_last_tab", m_saveLastTabCheckBox->isChecked()); + m_settings->setValue("theme", m_mainwindow->getThemeString(static_cast(m_themeComboBox->currentData(Qt::UserRole).toInt()))); + m_settings->setValue("show_hidden_files", m_showHiddenFilesCheckBox->isChecked()); + m_settings->setValue("highlight_color", m_highlightColor); + + if (!m_mainwindow->customThemeString().isEmpty()) + m_settings->setValue("custom_theme", m_customThemePath); +} + +void ViewOptionsDialog::onAccepted() +{ + MainWindow::Theme newTheme = static_cast(m_themeComboBox->currentData(Qt::UserRole).toInt()); + + m_mainwindow->setTheme(newTheme); + + saveViewOptions(); +} + +void ViewOptionsDialog::onRejected() +{ + loadViewOptions(); +} + +void ViewOptionsDialog::showDialog() +{ + loadViewOptions(); + show(); +} + +void ViewOptionsDialog::hideDialog() +{ + reject(); +} + +CoreInfoWidget::CoreInfoWidget(CoreInfoLabel *label, QWidget *parent) : + QWidget(parent) + ,m_label(label) + ,m_scrollArea(new QScrollArea(this)) +{ + m_scrollArea->setFrameShape(QFrame::NoFrame); + m_scrollArea->setWidgetResizable(true); + m_scrollArea->setWidget(m_label); +} + +QSize CoreInfoWidget::sizeHint() const +{ + return QSize(256, 256); +} + +void CoreInfoWidget::resizeEvent(QResizeEvent *event) +{ + QWidget::resizeEvent(event); + m_scrollArea->resize(event->size()); +} + +LogTextEdit::LogTextEdit(QWidget *parent) : + QPlainTextEdit(parent) +{ + +} + +void LogTextEdit::appendMessage(const QString& text) +{ + if (text.isEmpty()) + return; + + appendPlainText(text); + verticalScrollBar()->setValue(verticalScrollBar()->maximum()); +} + +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent) + ,m_loadCoreWindow(new LoadCoreWindow(this)) + ,m_timer(new QTimer(this)) + ,m_currentCore() + ,m_currentCoreVersion() + ,m_statusLabel(new QLabel(this)) + ,m_dirTree(new TreeView(this)) + ,m_dirModel(new QFileSystemModel(m_dirTree)) + ,m_listWidget(new QListWidget(this)) + ,m_tableWidget(new TableWidget(this)) + ,m_searchWidget(new QWidget(this)) + ,m_searchLineEdit(new QLineEdit(this)) + ,m_searchDock(new QDockWidget(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_EDIT_SEARCH), this)) + ,m_playlistFiles() + ,m_launchWithComboBox(new QComboBox(this)) + ,m_startCorePushButton(new QToolButton(this)) + ,m_coreInfoPushButton(new QToolButton(this)) + ,m_runPushButton(new QToolButton(this)) + ,m_stopPushButton(new QToolButton(this)) + ,m_browserAndPlaylistTabWidget(new QTabWidget(this)) + ,m_pendingRun(false) + ,m_thumbnailPixmap(NULL) + ,m_thumbnailPixmap2(NULL) + ,m_thumbnailPixmap3(NULL) + ,m_fileSanitizerRegex("[&*/:`<>?\\|]") + ,m_settings(NULL) + ,m_viewOptionsDialog(NULL) + ,m_coreInfoDialog(new CoreInfoDialog(this, NULL)) + ,m_defaultStyle(NULL) + ,m_defaultPalette() + ,m_currentTheme(THEME_SYSTEM_DEFAULT) + ,m_coreInfoDock(new QDockWidget(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CORE_INFO), this)) + ,m_coreInfoLabel(new CoreInfoLabel(QString(), this)) + ,m_coreInfoWidget(new CoreInfoWidget(m_coreInfoLabel, this)) + ,m_logDock(new QDockWidget(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_LOG), this)) + ,m_logWidget(new QWidget(this)) + ,m_logTextEdit(new LogTextEdit(m_logWidget)) + ,m_imageFormats() + ,m_historyPlaylistsItem(NULL) + ,m_folderIcon() + ,m_customThemeString() +{ + settings_t *settings = config_get_ptr(); + QDir playlistDir(settings->paths.directory_playlist); + QString configDir = QFileInfo(path_get(RARCH_PATH_CONFIG)).dir().absolutePath(); + QToolButton *searchResetButton = NULL; + + m_tableWidget->setAlternatingRowColors(true); + + m_logWidget->setObjectName("logWidget"); + + m_folderIcon = QIcon(QString(settings->paths.directory_assets) + GENERIC_FOLDER_ICON); + m_imageFormats = QVector::fromList(QImageReader::supportedImageFormats()); + m_defaultStyle = QApplication::style(); + m_defaultPalette = QApplication::palette(); + + /* ViewOptionsDialog needs m_settings set before it's constructed */ + m_settings = new QSettings(configDir + "/retroarch_qt.cfg", QSettings::IniFormat, this); + m_viewOptionsDialog = new ViewOptionsDialog(this, 0); + + /* default NULL parameter for parent wasn't added until 5.7 */ + m_startCorePushButton->setDefaultAction(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_START_CORE), m_startCorePushButton)); + m_startCorePushButton->setFixedSize(m_startCorePushButton->sizeHint()); + + m_runPushButton->setDefaultAction(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_RUN), m_runPushButton)); + m_runPushButton->setFixedSize(m_runPushButton->sizeHint()); + + m_stopPushButton->setDefaultAction(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_STOP), m_stopPushButton)); + m_stopPushButton->setFixedSize(m_stopPushButton->sizeHint()); + + m_coreInfoPushButton->setDefaultAction(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_INFO), m_coreInfoPushButton)); + m_coreInfoPushButton->setFixedSize(m_coreInfoPushButton->sizeHint()); + + searchResetButton = new QToolButton(m_searchWidget); + searchResetButton->setDefaultAction(new QAction("CLR", searchResetButton)); + searchResetButton->setFixedSize(searchResetButton->sizeHint()); + + connect(searchResetButton, SIGNAL(clicked()), this, SLOT(onSearchResetClicked())); + + m_dirModel->setFilter(QDir::NoDotAndDotDot | + QDir::AllDirs | + QDir::Drives | + (m_settings->value("show_hidden_files", true).toBool() ? (QDir::Hidden | QDir::System) : static_cast(0))); + +#if defined(Q_OS_WIN) + m_dirModel->setRootPath(""); +#else + m_dirModel->setRootPath("/"); +#endif + + m_dirTree->setModel(m_dirModel); + m_dirTree->setSelectionMode(QAbstractItemView::SingleSelection); + + if (m_dirModel->columnCount() > 3) + { + /* size */ + m_dirTree->hideColumn(1); + /* type */ + m_dirTree->hideColumn(2); + /* date modified */ + m_dirTree->hideColumn(3); + } + + m_dirTree->setCurrentIndex(m_dirModel->index(settings->paths.directory_menu_content)); + m_dirTree->scrollTo(m_dirTree->currentIndex(), QAbstractItemView::PositionAtTop); + m_dirTree->expand(m_dirTree->currentIndex()); + + reloadPlaylists(); + + m_searchWidget->setLayout(new QHBoxLayout()); + m_searchWidget->layout()->addWidget(m_searchLineEdit); + m_searchWidget->layout()->addWidget(searchResetButton); + + m_searchDock->setObjectName("searchDock"); + m_searchDock->setProperty("default_area", Qt::LeftDockWidgetArea); + m_searchDock->setProperty("menu_text", msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SEARCH)); + m_searchDock->setWidget(m_searchWidget); + + addDockWidget(static_cast(m_searchDock->property("default_area").toInt()), m_searchDock); + + m_coreInfoLabel->setAlignment(Qt::AlignLeft | Qt::AlignTop); + m_coreInfoLabel->setTextFormat(Qt::RichText); + + m_coreInfoDock->setObjectName("coreInfoDock"); + m_coreInfoDock->setProperty("default_area", Qt::RightDockWidgetArea); + m_coreInfoDock->setProperty("menu_text", msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CORE_INFO)); + m_coreInfoDock->setWidget(m_coreInfoWidget); + + addDockWidget(static_cast(m_coreInfoDock->property("default_area").toInt()), m_coreInfoDock); + + m_logWidget->setLayout(new QVBoxLayout()); + m_logWidget->layout()->addWidget(m_logTextEdit); + + m_logDock->setObjectName("logDock"); + m_logDock->setProperty("default_area", Qt::BottomDockWidgetArea); + m_logDock->setProperty("menu_text", msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_LOG)); + m_logDock->setWidget(m_logWidget); + + addDockWidget(static_cast(m_logDock->property("default_area").toInt()), m_logDock); + + /* Hide the log by default. If user has saved their dock positions with the log visible, + * then this hide() call will be reversed later by restoreState(). + * FIXME: If user unchecks "save dock positions", the log will not be unhidden even if + * it was previously saved in the config. + */ + m_logDock->hide(); + + m_dirTree->setContextMenuPolicy(Qt::CustomContextMenu); + m_listWidget->setContextMenuPolicy(Qt::CustomContextMenu); + + connect(m_searchLineEdit, SIGNAL(returnPressed()), this, SLOT(onSearchEnterPressed())); + connect(m_searchLineEdit, SIGNAL(textEdited(const QString&)), this, SLOT(onSearchLineEditEdited(const QString&))); + connect(m_timer, SIGNAL(timeout()), this, SLOT(onTimeout())); + connect(m_loadCoreWindow, SIGNAL(coreLoaded()), this, SLOT(onCoreLoaded())); + connect(m_loadCoreWindow, SIGNAL(windowClosed()), this, SLOT(onCoreLoadWindowClosed())); + connect(m_listWidget, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), this, SLOT(onCurrentListItemChanged(QListWidgetItem*, QListWidgetItem*))); + connect(m_tableWidget, SIGNAL(currentItemChanged(QTableWidgetItem*, QTableWidgetItem*)), this, SLOT(onCurrentTableItemChanged(QTableWidgetItem*, QTableWidgetItem*))); + connect(m_tableWidget, SIGNAL(itemDoubleClicked(QTableWidgetItem*)), this, SLOT(onContentItemDoubleClicked(QTableWidgetItem*))); + connect(m_tableWidget, SIGNAL(enterPressed()), this, SLOT(onTableWidgetEnterPressed())); + connect(m_startCorePushButton, SIGNAL(clicked()), this, SLOT(onStartCoreClicked())); + connect(m_coreInfoPushButton, SIGNAL(clicked()), m_coreInfoDialog, SLOT(showCoreInfo())); + connect(m_runPushButton, SIGNAL(clicked()), this, SLOT(onRunClicked())); + connect(m_stopPushButton, SIGNAL(clicked()), this, SLOT(onStopClicked())); + connect(m_browserAndPlaylistTabWidget, SIGNAL(currentChanged(int)), this, SLOT(onTabWidgetIndexChanged(int))); + connect(m_dirTree, SIGNAL(itemsSelected(QModelIndexList)), this, SLOT(onTreeViewItemsSelected(QModelIndexList))); + connect(m_dirTree, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(onFileBrowserTreeContextMenuRequested(const QPoint&))); + connect(m_listWidget, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(onPlaylistWidgetContextMenuRequested(const QPoint&))); + connect(m_launchWithComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onLaunchWithComboBoxIndexChanged(int))); + + /* make sure these use an auto connection so it will be queued if called from a different thread (some facilities in RA log messages from other threads) */ + connect(this, SIGNAL(gotLogMessage(const QString&)), this, SLOT(onGotLogMessage(const QString&)), Qt::AutoConnection); + connect(this, SIGNAL(gotStatusMessage(QString,unsigned,unsigned,bool)), this, SLOT(onGotStatusMessage(QString,unsigned,unsigned,bool)), Qt::AutoConnection); + connect(this, SIGNAL(gotReloadPlaylists()), this, SLOT(onGotReloadPlaylists())); + + m_timer->start(TIMER_MSEC); + + statusBar()->addPermanentWidget(m_statusLabel); + + setCurrentCoreLabel(); + setCoreActions(); + + /* both of these are necessary to get the folder to scroll to the top of the view */ + qApp->processEvents(); + QTimer::singleShot(0, this, SLOT(onBrowserStartClicked())); + + m_listWidget->setCurrentRow(0); + m_searchLineEdit->setFocus(); + m_loadCoreWindow->setWindowModality(Qt::ApplicationModal); + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) + resizeDocks(QList() << m_searchDock, QList() << 1, Qt::Vertical); +#endif +} + +MainWindow::~MainWindow() +{ + if (m_thumbnailPixmap) + delete m_thumbnailPixmap; + if (m_thumbnailPixmap2) + delete m_thumbnailPixmap2; + if (m_thumbnailPixmap3) + delete m_thumbnailPixmap3; +} + +void MainWindow::showWelcomeScreen() +{ + const QString welcomeText = QStringLiteral("" + "Welcome to the RetroArch Desktop Menu!
\n" + "
\n" + "Many settings and actions are currently only available in the familiar Big Picture menu,
\n" + "but this Desktop Menu should be functional for launching content and managing playlists.
\n" + "
\n" + "Some useful hotkeys for interacting with the Big Picture menu include:\n" + "
  • F1 - Bring up the Big Picture menu
  • \n" + "
  • F - Switch between fullscreen and windowed modes
  • \n" + "
  • F5 - Bring the Desktop Menu back if closed
  • \n" + "
  • Esc - Exit RetroArch
\n" + "\n" + "For more hotkeys and their assignments, see: Settings -> Input -> Input Hotkey Binds
\n" + "
\n" + "Documentation for RetroArch, libretro and cores:
\n" + "https://docs.libretro.com/"); + + if (!ui_window.qtWindow->settings()->value("show_welcome_screen", true).toBool()) + return; + + if (!ui_window.qtWindow->showMessageBox(welcomeText, MainWindow::MSGBOX_TYPE_INFO, Qt::ApplicationModal)) + ui_window.qtWindow->settings()->setValue("show_welcome_screen", false); + +} + +const QString& MainWindow::customThemeString() const +{ + return m_customThemeString; +} + +bool MainWindow::setCustomThemeFile(QString filePath) +{ + if (filePath.isEmpty()) + { + QMessageBox::critical(this, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CUSTOM_THEME), msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_FILE_PATH_IS_BLANK)); + return false; + } + else + { + QFile file(filePath); + + if (file.exists()) + { + bool opened = file.open(QIODevice::ReadOnly); + + if (opened) + { + QByteArray fileArray = file.readAll(); + QString fileStr = QString::fromUtf8(fileArray); + + file.close(); + + if (fileStr.isEmpty()) + { + QMessageBox::critical(this, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CUSTOM_THEME), msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_FILE_IS_EMPTY)); + return false; + } + else + setCustomThemeString(fileStr); + } + else + { + QMessageBox::critical(this, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CUSTOM_THEME), msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_FILE_READ_OPEN_FAILED)); + return false; + } + } + else + { + QMessageBox::critical(this, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CUSTOM_THEME), msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_FILE_DOES_NOT_EXIST)); + return false; + } + } + + return true; +} + +void MainWindow::setCustomThemeString(QString qss) +{ + m_customThemeString = qss; +} + +bool MainWindow::showMessageBox(QString msg, MessageBoxType msgType, Qt::WindowModality modality) +{ + QScopedPointer msgBoxPtr; + QMessageBox *msgBox = NULL; + QCheckBox *checkBox = NULL; + + msgBoxPtr.reset(new QMessageBox(this)); + msgBox = msgBoxPtr.data(); + checkBox = new QCheckBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_DONT_SHOW_AGAIN), msgBox); + + msgBox->setWindowModality(modality); + msgBox->setTextFormat(Qt::RichText); + + /* QMessageBox::setCheckBox() is available since 5.2 */ + msgBox->setCheckBox(checkBox); + + switch (msgType) + { + case MSGBOX_TYPE_INFO: + { + msgBox->setIcon(QMessageBox::Information); + msgBox->setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_INFORMATION)); + break; + } + case MSGBOX_TYPE_WARNING: + { + msgBox->setIcon(QMessageBox::Warning); + msgBox->setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_WARNING)); + break; + } + case MSGBOX_TYPE_ERROR: + { + msgBox->setIcon(QMessageBox::Critical); + msgBox->setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_ERROR)); + break; + } + default: + break; + } + + msgBox->setText(msg); + msgBox->exec(); + + if (checkBox->isChecked()) + return false; + + return true; +} + +void MainWindow::onPlaylistWidgetContextMenuRequested(const QPoint&) +{ + settings_t *settings = config_get_ptr(); + QScopedPointer menu; + QScopedPointer associateMenu; + QScopedPointer hiddenPlaylistsMenu; + QScopedPointer hideAction; + QAction *selectedAction = NULL; + QPoint cursorPos = QCursor::pos(); + QListWidgetItem *selectedItem = m_listWidget->itemAt(m_listWidget->viewport()->mapFromGlobal(cursorPos)); + QDir playlistDir(settings->paths.directory_playlist); + QString playlistDirAbsPath = playlistDir.absolutePath(); + QString currentPlaylistDirPath; + QString currentPlaylistPath; + QString currentPlaylistFileName; + QByteArray currentPlaylistFileNameArray; + QFileInfo currentPlaylistFileInfo; + QMap coreList; + core_info_list_t *core_info_list = NULL; + union string_list_elem_attr attr = {0}; + struct string_list *stnames = NULL; + struct string_list *stcores = NULL; + unsigned i = 0; + int j = 0; + size_t found = 0; + const char *currentPlaylistFileNameData = NULL; + char new_playlist_names[PATH_MAX_LENGTH]; + char new_playlist_cores[PATH_MAX_LENGTH]; + bool specialPlaylist = false; + bool foundHiddenPlaylist = false; + + if (!selectedItem) + return; + + new_playlist_names[0] = new_playlist_cores[0] = '\0'; + + stnames = string_split(settings->arrays.playlist_names, ";"); + stcores = string_split(settings->arrays.playlist_cores, ";"); + + currentPlaylistPath = selectedItem->data(Qt::UserRole).toString(); + currentPlaylistFileInfo = QFileInfo(currentPlaylistPath); + currentPlaylistFileName = currentPlaylistFileInfo.fileName(); + currentPlaylistDirPath = currentPlaylistFileInfo.absoluteDir().absolutePath(); + + currentPlaylistFileNameArray.append(currentPlaylistFileName); + currentPlaylistFileNameData = currentPlaylistFileNameArray.constData(); + + menu.reset(new QMenu(this)); + menu->setObjectName("menu"); + + hiddenPlaylistsMenu.reset(new QMenu(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_HIDDEN_PLAYLISTS), this)); + hiddenPlaylistsMenu->setObjectName("hiddenPlaylistsMenu"); + + hideAction.reset(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_HIDE), this)); + + menu->addAction(hideAction.data()); + + if (m_listWidget->count() > 0) + { + for (j = 0; j < m_listWidget->count(); j++) + { + QListWidgetItem *item = m_listWidget->item(j); + bool hidden = m_listWidget->isItemHidden(item); + + if (hidden) + { + QAction *action = hiddenPlaylistsMenu->addAction(item->text()); + action->setProperty("row", j); + action->setProperty("core_path", item->data(Qt::UserRole).toString()); + foundHiddenPlaylist = true; + } + } + } + + if (!foundHiddenPlaylist) + { + QAction *action = hiddenPlaylistsMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NONE)); + action->setProperty("row", -1); + } + + menu->addMenu(hiddenPlaylistsMenu.data()); + + if (currentPlaylistDirPath != playlistDirAbsPath) + { + /* special playlists like history etc. can't have an association */ + specialPlaylist = true; + } + + if (!specialPlaylist) + { + associateMenu.reset(new QMenu(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_ASSOCIATE_CORE), this)); + associateMenu->setObjectName("associateMenu"); + + core_info_get_list(&core_info_list); + + for (i = 0; i < core_info_list->count; i++) + { + const core_info_t *core = &core_info_list->list[i]; + coreList[core->core_name] = core; + } + + foreach (const QString &key, coreList.keys()) + { + const core_info_t *core = coreList.value(key); + QAction *action = associateMenu->addAction(core->core_name); + action->setProperty("core_path", core->path); + } + + menu->addMenu(associateMenu.data()); + } + + selectedAction = menu->exec(cursorPos); + + if (!selectedAction) + return; + + if (!specialPlaylist && selectedAction->parent() == associateMenu.data()) + { + found = string_list_find_elem(stnames, currentPlaylistFileNameData); + + if (found) + string_list_set(stcores, static_cast(found - 1), selectedAction->property("core_path").toString().toUtf8().constData()); + else + { + string_list_append(stnames, currentPlaylistFileNameData, attr); + string_list_append(stcores, "DETECT", attr); + + found = string_list_find_elem(stnames, currentPlaylistFileNameData); + + if (found) + string_list_set(stcores, static_cast(found - 1), selectedAction->property("core_path").toString().toUtf8().constData()); + } + + string_list_join_concat(new_playlist_names, + sizeof(new_playlist_names), stnames, ";"); + string_list_join_concat(new_playlist_cores, + sizeof(new_playlist_cores), stcores, ";"); + + strlcpy(settings->arrays.playlist_names, + new_playlist_names, sizeof(settings->arrays.playlist_names)); + strlcpy(settings->arrays.playlist_cores, + new_playlist_cores, sizeof(settings->arrays.playlist_cores)); + + if (stnames) + string_list_free(stnames); + if (stcores) + string_list_free(stcores); + } + else if (selectedAction == hideAction.data()) + { + int row = m_listWidget->row(selectedItem); + + if (row >= 0) + { + QStringList hiddenPlaylists = m_settings->value("hidden_playlists").toStringList(); + + if (!hiddenPlaylists.contains(currentPlaylistFileName)) + { + hiddenPlaylists.append(currentPlaylistFileName); + m_settings->setValue("hidden_playlists", hiddenPlaylists); + } + + m_listWidget->setRowHidden(row, true); + } + } + else if (selectedAction->parent() == hiddenPlaylistsMenu.data()) + { + QVariant rowVariant = selectedAction->property("row"); + + if (rowVariant.isValid()) + { + QStringList hiddenPlaylists = m_settings->value("hidden_playlists").toStringList(); + int row = rowVariant.toInt(); + + if (row >= 0) + { + QString playlistPath = selectedAction->property("core_path").toString(); + QFileInfo playlistFileInfo(playlistPath); + QString playlistFileName = playlistFileInfo.fileName(); + + if (hiddenPlaylists.contains(playlistFileName)) + { + hiddenPlaylists.removeOne(playlistFileName); + m_settings->setValue("hidden_playlists", hiddenPlaylists); + } + + m_listWidget->setRowHidden(row, false); + } + } + } + + setCoreActions(); +} + +void MainWindow::onFileBrowserTreeContextMenuRequested(const QPoint&) +{ +#ifdef HAVE_LIBRETRODB + QAction *action = NULL; + QList actions; + QScopedPointer scanAction; + QDir dir; + QString currentDirString = m_dirModel->filePath(m_dirTree->currentIndex()); + settings_t *settings = config_get_ptr(); + QByteArray dirArray; + const char *fullpath = NULL; + + if (currentDirString.isEmpty()) + return; + + dir = currentDirString; + + if (!dir.exists()) + return; + + /* default NULL parameter for parent wasn't added until 5.7 */ + scanAction.reset(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SCAN_DIRECTORY), 0)); + + actions.append(scanAction.data()); + + action = QMenu::exec(actions, QCursor::pos(), NULL, m_dirTree); + + if (!action) + return; + + dirArray = currentDirString.toUtf8(); + fullpath = dirArray.constData(); + + task_push_dbscan( + settings->paths.directory_playlist, + settings->paths.path_content_database, + fullpath, true, + m_settings->value("show_hidden_files", true).toBool(), + scan_finished_handler); +#endif +} + +void MainWindow::showStatusMessage(QString msg, unsigned priority, unsigned duration, bool flush) +{ + emit gotStatusMessage(msg, priority, duration, flush); +} + +void MainWindow::onGotStatusMessage(QString msg, unsigned priority, unsigned duration, bool flush) +{ + int msecDuration = 0; + QScreen *screen = qApp->primaryScreen(); + QStatusBar *status = statusBar(); + + if (msg.isEmpty()) + return; + + if (!status) + return; + + if (screen) + { + msecDuration = (duration / screen->refreshRate()) * 1000; + } + + if (msecDuration <= 0) + msecDuration = 1000; + + if (status->currentMessage().isEmpty() || flush) + status->showMessage(msg, msecDuration); +} + +void MainWindow::deferReloadPlaylists() +{ + emit gotReloadPlaylists(); +} + +void MainWindow::onGotReloadPlaylists() +{ + reloadPlaylists(); +} + +void MainWindow::reloadPlaylists() +{ + QListWidgetItem *allPlaylistsItem = NULL; + QListWidgetItem *favoritesPlaylistsItem = NULL; + QListWidgetItem *imagePlaylistsItem = NULL; + QListWidgetItem *musicPlaylistsItem = NULL; + QListWidgetItem *videoPlaylistsItem = NULL; + QListWidgetItem *firstItem = NULL; + QListWidgetItem *currentItem = NULL; + settings_t *settings = config_get_ptr(); + QDir playlistDir(settings->paths.directory_playlist); + QString currentPlaylistPath; + QStringList hiddenPlaylists = m_settings->value("hidden_playlists").toStringList(); + + currentItem = m_listWidget->currentItem(); + + if (currentItem) + { + currentPlaylistPath = currentItem->data(Qt::UserRole).toString(); + } + + getPlaylistFiles(); + + m_listWidget->clear(); + + allPlaylistsItem = new QListWidgetItem(m_folderIcon, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_ALL_PLAYLISTS)); + allPlaylistsItem->setData(Qt::UserRole, ALL_PLAYLISTS_TOKEN); + + favoritesPlaylistsItem = new QListWidgetItem(m_folderIcon, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_FAVORITES_TAB)); + favoritesPlaylistsItem->setData(Qt::UserRole, settings->paths.path_content_favorites); + + m_historyPlaylistsItem = new QListWidgetItem(m_folderIcon, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_HISTORY_TAB)); + m_historyPlaylistsItem->setData(Qt::UserRole, settings->paths.path_content_history); + + imagePlaylistsItem = new QListWidgetItem(m_folderIcon, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_IMAGES_TAB)); + imagePlaylistsItem->setData(Qt::UserRole, settings->paths.path_content_image_history); + + musicPlaylistsItem = new QListWidgetItem(m_folderIcon, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_MUSIC_TAB)); + musicPlaylistsItem->setData(Qt::UserRole, settings->paths.path_content_music_history); + + videoPlaylistsItem = new QListWidgetItem(m_folderIcon, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_TAB)); + videoPlaylistsItem->setData(Qt::UserRole, settings->paths.path_content_video_history); + + m_listWidget->addItem(allPlaylistsItem); + m_listWidget->addItem(favoritesPlaylistsItem); + m_listWidget->addItem(m_historyPlaylistsItem); + m_listWidget->addItem(imagePlaylistsItem); + m_listWidget->addItem(musicPlaylistsItem); + m_listWidget->addItem(videoPlaylistsItem); + + if (hiddenPlaylists.contains(ALL_PLAYLISTS_TOKEN)) + m_listWidget->setRowHidden(m_listWidget->row(allPlaylistsItem), true); + if (hiddenPlaylists.contains(QFileInfo(settings->paths.path_content_favorites).fileName())) + m_listWidget->setRowHidden(m_listWidget->row(favoritesPlaylistsItem), true); + if (hiddenPlaylists.contains(QFileInfo(settings->paths.path_content_history).fileName())) + m_listWidget->setRowHidden(m_listWidget->row(m_historyPlaylistsItem), true); + if (hiddenPlaylists.contains(QFileInfo(settings->paths.path_content_image_history).fileName())) + m_listWidget->setRowHidden(m_listWidget->row(imagePlaylistsItem), true); + if (hiddenPlaylists.contains(QFileInfo(settings->paths.path_content_music_history).fileName())) + m_listWidget->setRowHidden(m_listWidget->row(musicPlaylistsItem), true); + if (hiddenPlaylists.contains(QFileInfo(settings->paths.path_content_video_history).fileName())) + m_listWidget->setRowHidden(m_listWidget->row(videoPlaylistsItem), true); + + foreach (QString file, m_playlistFiles) + { + QListWidgetItem *item = NULL; + QString fileDisplayName = file; + QString fileName = file; + bool hasIcon = false; + QIcon icon; + QString iconPath; + + fileDisplayName.remove(file_path_str(FILE_PATH_LPL_EXTENSION)); + + iconPath = QString(settings->paths.directory_assets) + ICON_PATH + fileDisplayName + ".png"; + + hasIcon = QFile::exists(iconPath); + + if (hasIcon) + icon = QIcon(iconPath); + else + icon = m_folderIcon; + + item = new QListWidgetItem(icon, fileDisplayName); + item->setData(Qt::UserRole, playlistDir.absoluteFilePath(file)); + + m_listWidget->addItem(item); + + if (hiddenPlaylists.contains(fileName)) + { + int row = m_listWidget->row(item); + + if (row >= 0) + m_listWidget->setRowHidden(row, true); + } + } + + if (m_listWidget->count() > 0) + { + firstItem = m_listWidget->item(0); + + if (firstItem) + { + int i = 0; + bool found = false; + + for (i = 0; i < m_listWidget->count(); i++) + { + QListWidgetItem *item = m_listWidget->item(i); + QString path; + + if (item) + { + path = item->data(Qt::UserRole).toString(); + + if (!currentPlaylistPath.isEmpty() && !path.isEmpty()) + { + if (path == currentPlaylistPath) + { + found = true; + m_listWidget->setCurrentItem(item); + break; + } + } + } + } + + /* the previous playlist must be gone now, just select the first one */ + if (!found) + m_listWidget->setCurrentItem(firstItem); + } + } +} + +void MainWindow::appendLogMessage(const QString &msg) +{ + emit gotLogMessage(msg); +} + +void MainWindow::onGotLogMessage(const QString &msg) +{ + QString newMsg = msg; + + if (newMsg.at(newMsg.size() - 1) == '\n') + newMsg.chop(1); + + m_logTextEdit->appendMessage(newMsg); +} + +void MainWindow::onLaunchWithComboBoxIndexChanged(int) +{ + QList > infoList = getCoreInfo(); + QString coreInfoText; + QVariantMap coreMap = m_launchWithComboBox->currentData(Qt::UserRole).value(); + CoreSelection coreSelection = static_cast(coreMap.value("core_selection").toInt()); + int i = 0; + + if (infoList.count() == 0) + return; + + for (i = 0; i < infoList.count(); i++) + { + const QHash &hash = infoList.at(i); + const QString &key = hash.value("html_key", hash.value("key")); + const QString &value = hash.value("html_value", hash.value("value")); + + if (!key.isEmpty()) + coreInfoText += key; + + if (!value.isEmpty()) + { + if (!key.isEmpty()) + coreInfoText += " "; + + coreInfoText += value; + } + + if (i < infoList.count() - 1) + coreInfoText += "
\n"; + } + + m_coreInfoLabel->setText(coreInfoText); + + if (coreSelection == CORE_SELECTION_LOAD_CORE) + { + onLoadCoreClicked(); + } + else + { + m_loadCoreWindow->setProperty("last_launch_with_index", m_launchWithComboBox->currentIndex()); + } +} + +MainWindow::Theme MainWindow::getThemeFromString(QString themeString) +{ + if (themeString == "default") + return THEME_SYSTEM_DEFAULT; + else if (themeString == "dark") + return THEME_DARK; + else if (themeString == "custom") + return THEME_CUSTOM; + + return THEME_SYSTEM_DEFAULT; +} + +QString MainWindow::getThemeString(Theme theme) +{ + switch (theme) + { + case THEME_SYSTEM_DEFAULT: + return "default"; + case THEME_DARK: + return "dark"; + case THEME_CUSTOM: + return "custom"; + default: + break; + } + + return "default"; +} + +MainWindow::Theme MainWindow::theme() +{ + return m_currentTheme; +} + +void MainWindow::setTheme(Theme theme) +{ + m_currentTheme = theme; + + switch(theme) + { + case THEME_SYSTEM_DEFAULT: + { + qApp->setStyleSheet(qt_theme_default_stylesheet); + + break; + } + case THEME_DARK: + { + qApp->setStyleSheet(qt_theme_dark_stylesheet.arg(m_settings->value("highlight_color", "palette(highlight)").toString())); + + break; + } + case THEME_CUSTOM: + { + qApp->setStyleSheet(m_customThemeString); + + break; + } + default: + break; + } +} + +QList > MainWindow::getCoreInfo() +{ + QList > infoList; + QHash currentCore = getSelectedCore(); + core_info_list_t *core_info_list = NULL; + const core_info_t *core_info = NULL; + unsigned i = 0; + + core_info_get_list(&core_info_list); + + for (i = 0; i < core_info_list->count; i++) + { + const core_info_t *core = &core_info_list->list[i]; + + if (currentCore["core_path"] == core->path) + { + core_info = core; + break; + } + } + + if (currentCore["core_path"].isEmpty() || !core_info || !core_info->config_data) + { + QHash hash; + + hash["key"] = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_CORE_INFORMATION_AVAILABLE); + hash["value"] = ""; + + infoList.append(hash); + + return infoList; + } + + if (core_info->core_name) + { + QHash hash; + + hash["key"] = QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_CORE_NAME)) + ":"; + hash["value"] = core_info->core_name; + + infoList.append(hash); + } + + if (core_info->display_name) + { + QHash hash; + + hash["key"] = QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_CORE_LABEL)) + ":"; + hash["value"] = core_info->display_name; + + infoList.append(hash); + } + + if (core_info->systemname) + { + QHash hash; + + hash["key"] = QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_SYSTEM_NAME)) + ":"; + hash["value"] = core_info->systemname; + + infoList.append(hash); + } + + if (core_info->system_manufacturer) + { + QHash hash; + + hash["key"] = QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_SYSTEM_MANUFACTURER)) + ":"; + hash["value"] = core_info->system_manufacturer; + + infoList.append(hash); + } + + if (core_info->categories_list) + { + QHash hash; + QString categories; + + for (i = 0; i < core_info->categories_list->size; i++) + { + categories += core_info->categories_list->elems[i].data; + + if (i < core_info->categories_list->size - 1) + categories += ", "; + } + + hash["key"] = QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_CATEGORIES)) + ":"; + hash["value"] = categories; + + infoList.append(hash); + } + + if (core_info->authors_list) + { + QHash hash; + QString authors; + + for (i = 0; i < core_info->authors_list->size; i++) + { + authors += core_info->authors_list->elems[i].data; + + if (i < core_info->authors_list->size - 1) + authors += ", "; + } + + hash["key"] = QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_AUTHORS)) + ":"; + hash["value"] = authors; + + infoList.append(hash); + } + + if (core_info->permissions_list) + { + QHash hash; + QString permissions; + + for (i = 0; i < core_info->permissions_list->size; i++) + { + permissions += core_info->permissions_list->elems[i].data; + + if (i < core_info->permissions_list->size - 1) + permissions += ", "; + } + + hash["key"] = QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_PERMISSIONS)) + ":"; + hash["value"] = permissions; + + infoList.append(hash); + } + + if (core_info->licenses_list) + { + QHash hash; + QString licenses; + + for (i = 0; i < core_info->licenses_list->size; i++) + { + licenses += core_info->licenses_list->elems[i].data; + + if (i < core_info->licenses_list->size - 1) + licenses += ", "; + } + + hash["key"] = QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_LICENSES)) + ":"; + hash["value"] = licenses; + + infoList.append(hash); + } + + if (core_info->supported_extensions_list) + { + QHash hash; + QString supported_extensions; + + for (i = 0; i < core_info->supported_extensions_list->size; i++) + { + supported_extensions += core_info->supported_extensions_list->elems[i].data; + + if (i < core_info->supported_extensions_list->size - 1) + supported_extensions += ", "; + } + + hash["key"] = QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_SUPPORTED_EXTENSIONS)) + ":"; + hash["value"] = supported_extensions; + + infoList.append(hash); + } + + if (core_info->firmware_count > 0) + { + core_info_ctx_firmware_t firmware_info; + bool update_missing_firmware = false; + bool set_missing_firmware = false; + settings_t *settings = config_get_ptr(); + + firmware_info.path = core_info->path; + firmware_info.directory.system = settings->paths.directory_system; + + rarch_ctl(RARCH_CTL_UNSET_MISSING_BIOS, NULL); + + update_missing_firmware = core_info_list_update_missing_firmware(&firmware_info, &set_missing_firmware); + + if (set_missing_firmware) + rarch_ctl(RARCH_CTL_SET_MISSING_BIOS, NULL); + + if (update_missing_firmware) + { + QHash hash; + + hash["key"] = QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_FIRMWARE)) + ":"; + hash["value"] = ""; + + infoList.append(hash); + + /* FIXME: This looks hacky and probably + * needs to be improved for good translation support. */ + + for (i = 0; i < core_info->firmware_count; i++) + { + if (core_info->firmware[i].desc) + { + QString labelText = "(!) "; + QString valueText; + QHash hash; + bool missing = false; + + if (core_info->firmware[i].missing) + { + missing = true; + labelText += msg_hash_to_str(MENU_ENUM_LABEL_VALUE_MISSING); + } + else + { + labelText += msg_hash_to_str(MENU_ENUM_LABEL_VALUE_PRESENT); + } + + labelText += ", "; + + if (core_info->firmware[i].optional) + { + labelText += msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OPTIONAL); + } + else + { + labelText += msg_hash_to_str(MENU_ENUM_LABEL_VALUE_REQUIRED); + } + + labelText += ":"; + + if (core_info->firmware[i].desc) + { + valueText = core_info->firmware[i].desc; + } + else + { + valueText = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_NAME); + } + + hash["key"] = labelText; + hash["value"] = valueText; + + if (missing) + { + QString style = "font-weight: bold; color: #ff0000"; + hash["label_style"] = style; + hash["value_style"] = style; + hash["html_key"] = "" + hash["key"] + ""; + hash["html_value"] = "" + hash["value"] + ""; + } + else + { + QString style = "font-weight: bold; color: rgb(0, 175, 0)"; + hash["label_style"] = style; + hash["value_style"] = style; + hash["html_key"] = "" + hash["key"] + ""; + hash["html_value"] = "" + hash["value"] + ""; + } + + infoList.append(hash); + } + } + } + } + + if (core_info->notes) + { + for (i = 0; i < core_info->note_list->size; i++) + { + QHash hash; + + hash["key"] = ""; + hash["value"] = core_info->note_list->elems[i].data; + + infoList.append(hash); + } + } + + return infoList; +} + +void MainWindow::onSearchResetClicked() +{ + m_searchLineEdit->clear(); + onSearchEnterPressed(); +} + +QToolButton* MainWindow::coreInfoPushButton() +{ + return m_coreInfoPushButton; +} + +void MainWindow::onTreeViewItemsSelected(QModelIndexList selectedIndexes) +{ + QString dir; + + if (selectedIndexes.isEmpty()) + return; + + dir = m_dirModel->filePath(selectedIndexes.first()); + + selectBrowserDir(dir); +} + +void MainWindow::selectBrowserDir(QString path) +{ + QStringList horizontal_header_labels; + QDir dir = path; + QStringList dirList; + int i = 0; + + horizontal_header_labels << msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_NAME); + + m_tableWidget->clear(); + m_tableWidget->setColumnCount(0); + m_tableWidget->setRowCount(0); + m_tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows); + m_tableWidget->setSelectionMode(QAbstractItemView::SingleSelection); + m_tableWidget->setSortingEnabled(false); + m_tableWidget->setColumnCount(1); + m_tableWidget->setRowCount(0); + m_tableWidget->setHorizontalHeaderLabels(horizontal_header_labels); + m_tableWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + m_tableWidget->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); + + dirList = dir.entryList(QDir::NoDotAndDotDot | + QDir::Readable | + QDir::Files | + (m_settings->value("show_hidden_files", true).toBool() ? (QDir::Hidden | QDir::System) : static_cast(0)), + QDir::Name); + + if (dirList.count() == 0) + return; + + m_tableWidget->setRowCount(dirList.count()); + + for (i = 0; i < dirList.count(); i++) + { + QString fileName = dirList.at(i); + QTableWidgetItem *item = new QTableWidgetItem(fileName); + QHash hash; + QString filePath(dir.absoluteFilePath(fileName)); + QFileInfo fileInfo(filePath); + + hash["path"] = filePath; + hash["label"] = hash["path"]; + hash["label_noext"] = fileInfo.fileName().remove(QString(".") + fileInfo.completeSuffix()); + hash["db_name"] = fileInfo.dir().dirName(); + + item->setData(Qt::UserRole, QVariant::fromValue >(hash)); + item->setFlags(item->flags() & ~Qt::ItemIsEditable); + + m_tableWidget->setItem(i, 0, item); + } + + m_tableWidget->setSortingEnabled(true); + m_tableWidget->resizeColumnsToContents(); + m_tableWidget->sortByColumn(0, Qt::AscendingOrder); + m_tableWidget->selectRow(0); + + onSearchEnterPressed(); +} + +QTabWidget* MainWindow::browserAndPlaylistTabWidget() +{ + return m_browserAndPlaylistTabWidget; +} + +void MainWindow::onTableWidgetEnterPressed() +{ + onRunClicked(); +} + +void MainWindow::onContentItemDoubleClicked(QTableWidgetItem*) +{ + onRunClicked(); +} + +void MainWindow::onStartCoreClicked() +{ + content_ctx_info_t content_info; + + content_info.argc = 0; + content_info.argv = NULL; + content_info.args = NULL; + content_info.environ_get = NULL; + + path_clear(RARCH_PATH_BASENAME); + + if (!task_push_start_current_core(&content_info)) + { + QMessageBox::critical(this, msg_hash_to_str(MSG_ERROR), msg_hash_to_str(MSG_FAILED_TO_LOAD_CONTENT)); + } +} + +QHash MainWindow::getSelectedCore() +{ + QVariantMap coreMap = m_launchWithComboBox->currentData(Qt::UserRole).value(); + CoreSelection coreSelection = static_cast(coreMap.value("core_selection").toInt()); + QHash coreHash; + QHash contentHash; + QTableWidgetItem *contentItem = m_tableWidget->currentItem(); + + if (contentItem) + contentHash = contentItem->data(Qt::UserRole).value >(); + + switch(coreSelection) + { + case CORE_SELECTION_CURRENT: + { + coreHash["core_path"] = path_get(RARCH_PATH_CORE); + + break; + } + case CORE_SELECTION_PLAYLIST_SAVED: + { + if (!contentItem || contentHash["core_path"].isEmpty()) + break; + + coreHash["core_path"] = contentHash["core_path"]; + + break; + } + case CORE_SELECTION_PLAYLIST_DEFAULT: + { + QList > cores; + int i = 0; + + if (!contentItem || contentHash["db_name"].isEmpty()) + break; + + cores = getPlaylistDefaultCores(); + + for (i = 0; i < cores.count(); i++) + { + if (cores[i]["playlist_filename"] == contentHash["db_name"]) + { + if (cores[i]["core_path"].isEmpty()) + break; + + coreHash["core_path"] = cores[i]["core_path"]; + + break; + } + } + + break; + } + default: + break; + } + + return coreHash; +} + +void MainWindow::onRunClicked() +{ +#ifdef HAVE_MENU + content_ctx_info_t content_info; + QHash contentHash; + QTableWidgetItem *item = m_tableWidget->currentItem(); + QByteArray corePathArray; + QByteArray contentPathArray; + QByteArray contentLabelArray; + const char *corePath = NULL; + const char *contentPath = NULL; + const char *contentLabel = NULL; + QVariantMap coreMap = m_launchWithComboBox->currentData(Qt::UserRole).value(); + CoreSelection coreSelection = static_cast(coreMap.value("core_selection").toInt()); + + if (!item) + return; + + contentHash = item->data(Qt::UserRole).value >(); + + if (coreSelection == CORE_SELECTION_ASK) + { + QTableWidgetItem *item = m_tableWidget->currentItem(); + QStringList extensionFilters; + + if (item) + { + QHash hash; + + hash = item->data(Qt::UserRole).value >(); + + if (hash.contains("path")) + { + int lastIndex = hash["path"].lastIndexOf('.'); + QString extensionStr; + QByteArray pathArray = hash["path"].toUtf8(); + const char *pathData = pathArray.constData(); + + if (lastIndex >= 0) + { + extensionStr = hash["path"].mid(lastIndex + 1); + + if (!extensionStr.isEmpty()) + { + extensionFilters.append(extensionStr.toLower()); + } + } + + if (path_is_compressed_file(pathData)) + { + unsigned i = 0; + struct string_list *list = file_archive_get_file_list(pathData, NULL); + + if (list) + { + if (list->size > 0) + { + for (i = 0; i < list->size; i++) + { + const char *filePath = list->elems[i].data; + const char *extension = path_get_extension(filePath); + + if (!extensionFilters.contains(extension, Qt::CaseInsensitive)) + extensionFilters.append(extension); + } + } + + string_list_free(list); + } + } + } + } + + m_pendingRun = true; + onLoadCoreClicked(extensionFilters); + + return; + } + + switch(coreSelection) + { + case CORE_SELECTION_CURRENT: + { + corePathArray = path_get(RARCH_PATH_CORE); + contentPathArray = contentHash["path"].toUtf8(); + contentLabelArray = contentHash["label_noext"].toUtf8(); + + break; + } + case CORE_SELECTION_PLAYLIST_SAVED: + { + corePathArray = contentHash["core_path"].toUtf8(); + contentPathArray = contentHash["path"].toUtf8(); + contentLabelArray = contentHash["label_noext"].toUtf8(); + + break; + } + case CORE_SELECTION_PLAYLIST_DEFAULT: + { + QList > cores = getPlaylistDefaultCores(); + int i = 0; + + for (i = 0; i < cores.count(); i++) + { + if (cores[i]["playlist_filename"] == contentHash["db_name"]) + { + corePathArray = cores[i]["core_path"].toUtf8(); + contentPathArray = contentHash["path"].toUtf8(); + contentLabelArray = contentHash["label_noext"].toUtf8(); + break; + } + } + + break; + } + default: + return; + } + + corePath = corePathArray.constData(); + contentPath = contentPathArray.constData(); + contentLabel = contentLabelArray.constData(); + + content_info.argc = 0; + content_info.argv = NULL; + content_info.args = NULL; + content_info.environ_get = NULL; + + if (!task_push_load_content_from_playlist_from_menu( + corePath, contentPath, contentLabel, + &content_info, + NULL, NULL)) + { + QMessageBox::critical(this, msg_hash_to_str(MSG_ERROR), msg_hash_to_str(MSG_FAILED_TO_LOAD_CONTENT)); + return; + } + + menu_driver_ctl(RARCH_MENU_CTL_SET_PENDING_QUICK_MENU, NULL); +#endif +} + +bool MainWindow::isContentLessCore() +{ + rarch_system_info_t *system = runloop_get_system_info(); + + return system->load_no_content; +} + +bool MainWindow::isCoreLoaded() +{ + if (m_currentCore.isEmpty() || m_currentCore == msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_CORE)) + return false; + + return true; +} + +ViewOptionsDialog* MainWindow::viewOptionsDialog() +{ + return m_viewOptionsDialog; +} + +QList > MainWindow::getPlaylistDefaultCores() +{ + settings_t *settings = config_get_ptr(); + struct string_list *playlists = string_split(settings->arrays.playlist_names, ";"); + struct string_list *cores = string_split(settings->arrays.playlist_cores, ";"); + unsigned i = 0; + QList > coreList; + + if (!playlists || !cores) + { + RARCH_WARN("[Qt]: Could not parse one of playlist_names or playlist_cores\n"); + goto finish; + } + else if (playlists->size != cores->size) + { + RARCH_WARN("[Qt]: playlist_names array size differs from playlist_cores\n"); + goto finish; + } + + if (playlists->size == 0) + goto finish; + + for (i = 0; i < playlists->size; i++) + { + const char *playlist = playlists->elems[i].data; + const char *core = cores->elems[i].data; + QHash hash; + + hash["playlist_filename"] = playlist; + hash["playlist_filename"].remove(file_path_str(FILE_PATH_LPL_EXTENSION)); + hash["core_path"] = core; + + coreList.append(hash); + } + +finish: + if (playlists) + string_list_free(playlists); + if (cores) + string_list_free(cores); + + return coreList; +} + +void MainWindow::setCoreActions() +{ + QTableWidgetItem *currentContentItem = m_tableWidget->currentItem(); + QListWidgetItem *currentPlaylistItem = m_listWidget->currentItem(); + QHash hash; + + m_launchWithComboBox->clear(); + + if (isContentLessCore()) + m_startCorePushButton->show(); + else + m_startCorePushButton->hide(); + + if (isCoreLoaded()) + { + QVariantMap comboBoxMap; + comboBoxMap["core_name"] = m_currentCore; + comboBoxMap["core_path"] = path_get(RARCH_PATH_CORE); + comboBoxMap["core_selection"] = CORE_SELECTION_CURRENT; + m_launchWithComboBox->addItem(m_currentCore, QVariant::fromValue(comboBoxMap)); + } + + if (currentContentItem) + hash = currentContentItem->data(Qt::UserRole).value >(); + + if (m_browserAndPlaylistTabWidget->tabText(m_browserAndPlaylistTabWidget->currentIndex()) == msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_TAB_PLAYLISTS)) + { + if (currentContentItem) + { + QString coreName = hash["core_name"]; + + if (coreName.isEmpty()) + { + coreName = ""; + } + else + { + const char *detect_str = file_path_str(FILE_PATH_DETECT); + + if (coreName != detect_str) + { + if (m_launchWithComboBox->findText(coreName) == -1) + { + int i = 0; + bool found_existing = false; + + for (i = 0; i < m_launchWithComboBox->count(); i++) + { + QVariantMap map = m_launchWithComboBox->itemData(i, Qt::UserRole).toMap(); + + if (map.value("core_path").toString() == hash["core_path"] || map.value("core_name").toString() == coreName) + { + found_existing = true; + break; + } + } + + if (!found_existing) + { + QVariantMap comboBoxMap; + comboBoxMap["core_name"] = coreName; + comboBoxMap["core_path"] = hash["core_path"]; + comboBoxMap["core_selection"] = CORE_SELECTION_PLAYLIST_SAVED; + m_launchWithComboBox->addItem(coreName, QVariant::fromValue(comboBoxMap)); + } + } + } + } + } + } + + if (!hash["db_name"].isEmpty()) + { + QList > defaultCores = getPlaylistDefaultCores(); + int i = 0; + + if (defaultCores.count() > 0) + { + QString currentPlaylistItemDataString; + QHash hash; + bool allPlaylists = false; + int row = 0; + + if (currentPlaylistItem) + { + currentPlaylistItemDataString = currentPlaylistItem->data(Qt::UserRole).toString(); + allPlaylists = (currentPlaylistItemDataString == ALL_PLAYLISTS_TOKEN); + } + + if (currentContentItem) + hash = currentContentItem->data(Qt::UserRole).value >(); + + for (row = 0; row < m_listWidget->count(); row++) + { + if (allPlaylists) + { + QListWidgetItem *listItem = m_listWidget->item(row); + QString listItemString = listItem->data(Qt::UserRole).toString(); + QFileInfo info; + + info.setFile(listItemString); + + if (listItemString == ALL_PLAYLISTS_TOKEN) + continue; + } + + for (i = 0; i < defaultCores.count(); i++) + { + QString playlist = defaultCores.at(i)["playlist_filename"]; + QString core = defaultCores.at(i)["core_path"]; + QString currentPlaylistFileName = hash["db_name"]; + + playlist.remove(file_path_str(FILE_PATH_LPL_EXTENSION)); + + if (currentPlaylistFileName == playlist) + { + core_info_list_t *coreInfoList = NULL; + unsigned j = 0; + + core_info_get_list(&coreInfoList); + + for (j = 0; j < coreInfoList->count; j++) + { + const core_info_t *info = &coreInfoList->list[j]; + + if (core == info->path) + { + if (m_launchWithComboBox->findText(info->core_name) == -1) + { + int i = 0; + bool found_existing = false; + + for (i = 0; i < m_launchWithComboBox->count(); i++) + { + QVariantMap map = m_launchWithComboBox->itemData(i, Qt::UserRole).toMap(); + + if (map.value("core_path").toString() == info->path || map.value("core_name").toString() == info->core_name) + { + found_existing = true; + break; + } + } + + if (!found_existing) + { + QVariantMap comboBoxMap; + comboBoxMap["core_name"] = info->core_name; + comboBoxMap["core_path"] = info->path; + comboBoxMap["core_selection"] = CORE_SELECTION_PLAYLIST_DEFAULT; + m_launchWithComboBox->addItem(info->core_name, QVariant::fromValue(comboBoxMap)); + } + } + } + } + } + } + + if (!allPlaylists) + break; + } + } + } + + { + QVariantMap comboBoxMap; + comboBoxMap["core_selection"] = CORE_SELECTION_ASK; + m_launchWithComboBox->addItem(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CORE_SELECTION_ASK), QVariant::fromValue(comboBoxMap)); + m_launchWithComboBox->insertSeparator(m_launchWithComboBox->count()); + comboBoxMap["core_selection"] = CORE_SELECTION_LOAD_CORE; + m_launchWithComboBox->addItem(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_LOAD_CORE)) + "...", QVariant::fromValue(comboBoxMap)); + } +} + +void MainWindow::onTabWidgetIndexChanged(int index) +{ + Q_UNUSED(index) + + if (m_browserAndPlaylistTabWidget->tabText(m_browserAndPlaylistTabWidget->currentIndex()) == msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_TAB_FILE_BROWSER)) + { + QModelIndex index = m_dirTree->currentIndex(); + + m_tableWidget->clear(); + m_tableWidget->setColumnCount(0); + m_tableWidget->setRowCount(0); + + if (index.isValid()) + { + m_dirTree->clearSelection(); + m_dirTree->setCurrentIndex(index); + } + } + else if (m_browserAndPlaylistTabWidget->tabText(m_browserAndPlaylistTabWidget->currentIndex()) == msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_TAB_PLAYLISTS)) + { + QListWidgetItem *item = m_listWidget->currentItem(); + + m_tableWidget->clear(); + m_tableWidget->setColumnCount(0); + m_tableWidget->setRowCount(0); + + if (item) + { + m_listWidget->setCurrentItem(NULL); + m_listWidget->setCurrentItem(item); + } + } + + setCoreActions(); +} + +QToolButton* MainWindow::runPushButton() +{ + return m_runPushButton; +} + +QToolButton* MainWindow::stopPushButton() +{ + return m_stopPushButton; +} + +QToolButton* MainWindow::startCorePushButton() +{ + return m_startCorePushButton; +} + +QComboBox* MainWindow::launchWithComboBox() +{ + return m_launchWithComboBox; +} + +void MainWindow::getPlaylistFiles() +{ + settings_t *settings = config_get_ptr(); + QDir playlistDir(settings->paths.directory_playlist); + + m_playlistFiles = playlistDir.entryList(QDir::NoDotAndDotDot | QDir::Readable | QDir::Files, QDir::Name); +} + +void MainWindow::onSearchLineEditEdited(const QString &text) +{ + int i = 0; + QList items; + QVector textUnicode = text.toUcs4(); + QVector textHiraToKata; + QVector textKataToHira; + bool foundHira = false; + bool foundKata = false; + + if (text.isEmpty()) + { + for (i = 0; i < m_tableWidget->rowCount(); i++) + { + m_tableWidget->setRowHidden(i, false); + } + return; + } + + items.append(m_tableWidget->findItems(text, Qt::MatchContains)); + + for (i = 0; i < textUnicode.size(); i++) + { + unsigned code = textUnicode.at(i); + + if (code >= HIRAGANA_START && code <= HIRAGANA_END) + { + foundHira = true; + textHiraToKata += code + HIRA_KATA_OFFSET; + } + else if (code >= KATAKANA_START && code <= KATAKANA_END) + { + foundKata = true; + textKataToHira += code - HIRA_KATA_OFFSET; + } + else + { + textHiraToKata += code; + textKataToHira += code; + } + } + + if (foundHira) + { + items.append(m_tableWidget->findItems(QString::fromUcs4(textHiraToKata.constData(), textHiraToKata.size()), Qt::MatchContains)); + } + + if (foundKata) + { + items.append(m_tableWidget->findItems(QString::fromUcs4(textKataToHira.constData(), textKataToHira.size()), Qt::MatchContains)); + } + + if (items.isEmpty()) + { + for (i = 0; i < m_tableWidget->rowCount(); i++) + { + m_tableWidget->setRowHidden(i, true); + } + + return; + } + else + { + for (i = 0; i < m_tableWidget->rowCount(); i++) + { + if (items.contains(m_tableWidget->item(i, 0))) + m_tableWidget->setRowHidden(i, false); + else + m_tableWidget->setRowHidden(i, true); + } + } +} + +void MainWindow::onViewClosedDocksAboutToShow() +{ + QMenu *menu = qobject_cast(sender()); + QList dockWidgets; + bool found = false; + + if (!menu) + return; + + dockWidgets = findChildren(); + + menu->clear(); + + if (dockWidgets.isEmpty()) + { + menu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NONE)); + return; + } + + foreach (QDockWidget *dock, dockWidgets) + { + if (!dock->isVisible()) + { + QAction *action = menu->addAction(dock->property("menu_text").toString(), this, SLOT(onShowHiddenDockWidgetAction())); + action->setProperty("dock_name", dock->objectName()); + found = true; + } + } + + if (!found) + { + menu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NONE)); + } +} + +void MainWindow::onShowHiddenDockWidgetAction() +{ + QAction *action = qobject_cast(sender()); + QDockWidget *dock = NULL; + + if (!action) + return; + + dock = findChild(action->property("dock_name").toString()); + + if (!dock) + return; + + if (!dock->isVisible()) + { + addDockWidget(static_cast(dock->property("default_area").toInt()), dock); + dock->setVisible(true); + dock->setFloating(false); + } +} + +QWidget* MainWindow::searchWidget() +{ + return m_searchWidget; +} + +QLineEdit* MainWindow::searchLineEdit() +{ + return m_searchLineEdit; +} + +void MainWindow::onSearchEnterPressed() +{ + onSearchLineEditEdited(m_searchLineEdit->text()); +} + +void MainWindow::onCurrentTableItemChanged(QTableWidgetItem *current, QTableWidgetItem *) +{ + settings_t *settings = config_get_ptr(); + QHash hash; + QString label; + QString playlist_name; + QByteArray extension; + QString extensionStr; + int lastIndex = -1; + + if (!current) + return; + + hash = current->data(Qt::UserRole).value >(); + + label = hash["label_noext"]; + label.replace(m_fileSanitizerRegex, "_"); + + lastIndex = hash["path"].lastIndexOf('.'); + + if (lastIndex >= 0) + { + extensionStr = hash["path"].mid(lastIndex + 1); + + if (!extensionStr.isEmpty()) + { + extension = extensionStr.toLower().toUtf8(); + } + } + + playlist_name = hash["db_name"]; + + if (m_thumbnailPixmap) + delete m_thumbnailPixmap; + if (m_thumbnailPixmap2) + delete m_thumbnailPixmap2; + if (m_thumbnailPixmap3) + delete m_thumbnailPixmap3; + + if (!extension.isEmpty() && m_imageFormats.contains(extension)) + { + /* use thumbnail widgets to show regular image files */ + m_thumbnailPixmap = new QPixmap(hash["path"]); + m_thumbnailPixmap2 = new QPixmap(*m_thumbnailPixmap); + m_thumbnailPixmap3 = new QPixmap(*m_thumbnailPixmap); + } + else + { + m_thumbnailPixmap = new QPixmap(QString(settings->paths.directory_thumbnails) + "/" + playlist_name + "/" + THUMBNAIL_BOXART + "/" + label + ".png"); + m_thumbnailPixmap2 = new QPixmap(QString(settings->paths.directory_thumbnails) + "/" + playlist_name + "/" + THUMBNAIL_TITLE + "/" + label + ".png"); + m_thumbnailPixmap3 = new QPixmap(QString(settings->paths.directory_thumbnails) + "/" + playlist_name + "/" + THUMBNAIL_SCREENSHOT + "/" + label + ".png"); + } + + resizeThumbnails(true, true, true); + + setCoreActions(); +} + +void MainWindow::onResizeThumbnailOne() +{ + resizeThumbnails(true, false, false); +} + +void MainWindow::onResizeThumbnailTwo() +{ + resizeThumbnails(false, true, false); +} + +void MainWindow::onResizeThumbnailThree() +{ + resizeThumbnails(false, false, true); +} + +void MainWindow::resizeThumbnails(bool one, bool two, bool three) +{ + QPixmap pixmap; + QPixmap pixmap2; + QPixmap pixmap3; + ThumbnailLabel *thumbnail = NULL; + ThumbnailLabel *thumbnail2 = NULL; + ThumbnailLabel *thumbnail3 = NULL; + + if (m_thumbnailPixmap) + pixmap = *m_thumbnailPixmap; + if (m_thumbnailPixmap2) + pixmap2 = *m_thumbnailPixmap2; + if (m_thumbnailPixmap3) + pixmap3 = *m_thumbnailPixmap3; + + thumbnail = findChild("thumbnail"); + thumbnail2 = findChild("thumbnail2"); + thumbnail3 = findChild("thumbnail3"); + + if (thumbnail && one) + { + if (pixmap.isNull()) + thumbnail->hide(); + else + { + thumbnail->show(); + emit thumbnailChanged(pixmap); + thumbnail->update(); + } + } + + if (thumbnail2 && two) + { + if (pixmap2.isNull()) + thumbnail2->hide(); + else + { + thumbnail2->show(); + emit thumbnail2Changed(pixmap2); + thumbnail2->update(); + } + } + + if (thumbnail3 && three) + { + if (pixmap3.isNull()) + thumbnail3->hide(); + else + { + thumbnail3->show(); + emit thumbnail3Changed(pixmap3); + thumbnail3->update(); + } + } +} + +void MainWindow::onCurrentListItemChanged(QListWidgetItem *current, QListWidgetItem *previous) +{ + Q_UNUSED(current) + Q_UNUSED(previous) + + if (m_browserAndPlaylistTabWidget->tabText(m_browserAndPlaylistTabWidget->currentIndex()) != msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_TAB_PLAYLISTS)) + return; + + initContentTableWidget(); + setCoreActions(); +} + +TableWidget* MainWindow::contentTableWidget() +{ + return m_tableWidget; +} + +void MainWindow::onBrowserDownloadsClicked() +{ + settings_t *settings = config_get_ptr(); + QDir dir(settings->paths.directory_core_assets); + + m_dirTree->setCurrentIndex(m_dirModel->index(dir.absolutePath())); + /* for some reason, scrollTo only seems to work right when the button is clicked twice (only tested on Linux) */ + m_dirTree->scrollTo(m_dirTree->currentIndex(), QAbstractItemView::PositionAtTop); +} + +void MainWindow::onBrowserUpClicked() +{ + QDir dir(m_dirModel->filePath(m_dirTree->currentIndex())); + + dir.cdUp(); + + m_dirTree->setCurrentIndex(m_dirModel->index(dir.absolutePath())); + m_dirTree->scrollTo(m_dirTree->currentIndex(), QAbstractItemView::EnsureVisible); +} + +void MainWindow::onBrowserStartClicked() +{ + settings_t *settings = config_get_ptr(); + + m_dirTree->setCurrentIndex(m_dirModel->index(settings->paths.directory_menu_content)); + m_dirTree->scrollTo(m_dirTree->currentIndex(), QAbstractItemView::PositionAtTop); +} + +QListWidget* MainWindow::playlistListWidget() +{ + return m_listWidget; +} + +TreeView* MainWindow::dirTreeView() +{ + return m_dirTree; +} + +void MainWindow::onTimeout() +{ + bool contentless = false; + bool is_inited = false; + + content_get_status(&contentless, &is_inited); + + if (is_inited) + { + if (m_runPushButton->isVisible()) + m_runPushButton->hide(); + if (!m_stopPushButton->isVisible()) + m_stopPushButton->show(); + } + else + { + if (!m_runPushButton->isVisible()) + m_runPushButton->show(); + if (m_stopPushButton->isVisible()) + m_stopPushButton->hide(); + } + + setCurrentCoreLabel(); +} + +void MainWindow::onStopClicked() +{ + menu_navigation_set_selection(0); + command_event(CMD_EVENT_UNLOAD_CORE, NULL); + setCurrentCoreLabel(); +} + +void MainWindow::setCurrentCoreLabel() +{ + rarch_system_info_t *system = runloop_get_system_info(); + bool update = false; + QString libraryName = system->info.library_name; + const char *no_core_str = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_CORE); + + if (m_statusLabel->text().isEmpty() || (m_currentCore != no_core_str && libraryName.isEmpty())) + { + m_currentCore = no_core_str; + m_currentCoreVersion = ""; + update = true; + } + else + { + if (m_currentCore != libraryName && !libraryName.isEmpty()) + { + m_currentCore = system->info.library_name; + m_currentCoreVersion = (string_is_empty(system->info.library_version) ? "" : system->info.library_version); + update = true; + } + } + + if (update) + { + QAction *unloadCoreAction = findChild("unloadCoreAction"); + QString text = QString(PACKAGE_VERSION) + " - " + m_currentCore + " " + m_currentCoreVersion; + m_statusLabel->setText(text); + m_loadCoreWindow->setStatusLabel(text); + setCoreActions(); + + if (unloadCoreAction) + { + if (libraryName.isEmpty()) + unloadCoreAction->setEnabled(false); + else + unloadCoreAction->setEnabled(true); + } + } +} + +void MainWindow::onCoreLoadWindowClosed() +{ + QVariant lastLaunchWithVariant = m_loadCoreWindow->property("last_launch_with_index"); + int lastLaunchWithIndex = lastLaunchWithVariant.toInt(); + + m_pendingRun = false; + + if (lastLaunchWithVariant.isValid() && lastLaunchWithIndex >= 0) + { + m_launchWithComboBox->setCurrentIndex(lastLaunchWithIndex); + m_loadCoreWindow->setProperty("last_launch_with_index", -1); + } +} + +void MainWindow::onCoreLoaded() +{ + QAction *unloadAction = findChild("unloadCoreAction"); + + activateWindow(); + raise(); + setCurrentCoreLabel(); + setCoreActions(); + + if (unloadAction) + unloadAction->setEnabled(true); + + m_loadCoreWindow->hide(); + + if (m_pendingRun) + { + m_pendingRun = false; + onRunClicked(); + } +} + +void MainWindow::onUnloadCoreMenuAction() +{ + QAction *action = qobject_cast(sender()); + + menu_navigation_set_selection(0); + + if (!command_event(CMD_EVENT_UNLOAD_CORE, NULL)) + { + /* TODO */ + return; + } + + setCurrentCoreLabel(); + setCoreActions(); + + if (!action) + return; + + action->setEnabled(false); +} + +void MainWindow::onLoadCoreClicked(const QStringList &extensionFilters) +{ + m_loadCoreWindow->show(); + m_loadCoreWindow->resize(width() / 2, height()); + m_loadCoreWindow->setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, m_loadCoreWindow->size(), geometry())); + m_loadCoreWindow->initCoreList(extensionFilters); +} + +void MainWindow::initContentTableWidget() +{ + QListWidgetItem *item = m_listWidget->currentItem(); + QStringList horizontal_header_labels; + QString path; + + if (!item) + return; + + horizontal_header_labels << msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_NAME); + + m_tableWidget->clear(); + m_tableWidget->setColumnCount(0); + m_tableWidget->setRowCount(0); + m_tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows); + m_tableWidget->setSelectionMode(QAbstractItemView::SingleSelection); + m_tableWidget->setSortingEnabled(false); + m_tableWidget->setColumnCount(1); + m_tableWidget->setRowCount(0); + m_tableWidget->setHorizontalHeaderLabels(horizontal_header_labels); + m_tableWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + m_tableWidget->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); + + path = item->data(Qt::UserRole).toString(); + + if (path == ALL_PLAYLISTS_TOKEN) + { + settings_t *settings = config_get_ptr(); + QDir playlistDir(settings->paths.directory_playlist); + + foreach (QString playlist, m_playlistFiles) + { + addPlaylistItemsToTable(playlistDir.absoluteFilePath(playlist)); + } + } + else + addPlaylistItemsToTable(path); + + m_tableWidget->setSortingEnabled(true); + + if (item != m_historyPlaylistsItem) + m_tableWidget->sortByColumn(0, Qt::AscendingOrder); + + m_tableWidget->resizeColumnsToContents(); + m_tableWidget->selectRow(0); + + onSearchEnterPressed(); +} + +void MainWindow::addPlaylistItemsToTable(QString pathString) +{ + QByteArray pathArray; + const char *pathData = NULL; + playlist_t *playlist = NULL; + unsigned playlistSize = 0; + unsigned i = 0; + int oldRowCount = m_tableWidget->rowCount(); + + pathArray.append(pathString); + pathData = pathArray.constData(); + + playlist = playlist_init(pathData, COLLECTION_SIZE); + playlistSize = playlist_get_size(playlist); + + m_tableWidget->setRowCount(oldRowCount + playlistSize); + + for (i = 0; i < playlistSize; i++) + { + const char *path = NULL; + const char *label = NULL; + const char *core_path = NULL; + const char *core_name = NULL; + const char *crc32 = NULL; + const char *db_name = NULL; + QTableWidgetItem *labelItem = NULL; + QHash hash; + + playlist_get_index(playlist, i, + &path, &label, &core_path, + &core_name, &crc32, &db_name); + + if (string_is_empty(path)) + continue; + else + hash["path"] = path; + + if (string_is_empty(label)) + { + hash["label"] = path; + hash["label_noext"] = path; + } + else + { + hash["label"] = label; + hash["label_noext"] = label; + } + + if (!string_is_empty(core_path)) + hash["core_path"] = core_path; + + if (!string_is_empty(core_name)) + hash["core_name"] = core_name; + + if (!string_is_empty(crc32)) + hash["crc32"] = crc32; + + if (!string_is_empty(db_name)) + { + hash["db_name"] = db_name; + hash["db_name"].remove(file_path_str(FILE_PATH_LPL_EXTENSION)); + } + + labelItem = new QTableWidgetItem(hash["label"]); + labelItem->setData(Qt::UserRole, QVariant::fromValue >(hash)); + labelItem->setFlags(labelItem->flags() & ~Qt::ItemIsEditable); + + m_tableWidget->setItem(oldRowCount + i, 0, labelItem); + } + + playlist_free(playlist); + playlist = NULL; +} + +void MainWindow::keyPressEvent(QKeyEvent *event) +{ +/* + if (event->key() == Qt::Key_F5) + { + event->accept(); + hide(); + + return; + } +*/ + QMainWindow::keyPressEvent(event); +} + +QSettings* MainWindow::settings() +{ + return m_settings; +} + +void MainWindow::closeEvent(QCloseEvent *event) +{ + if (m_settings->value("save_geometry", false).toBool()) + m_settings->setValue("geometry", saveGeometry()); + if (m_settings->value("save_dock_positions", false).toBool()) + m_settings->setValue("dock_positions", saveState()); + if (m_settings->value("save_last_tab", false).toBool()) + m_settings->setValue("last_tab", m_browserAndPlaylistTabWidget->currentIndex()); + + QMainWindow::closeEvent(event); +} + +static void* ui_window_qt_init(void) +{ + ui_window.qtWindow = new MainWindow(); + + return &ui_window; +} static void ui_window_qt_destroy(void *data) { + (void)data; +/* ui_window_qt_t *window = (ui_window_qt_t*)data; delete window->qtWindow; +*/ } static void ui_window_qt_set_focused(void *data) { + (void)data; +/* ui_window_qt_t *window = (ui_window_qt_t*)data; window->qtWindow->raise(); window->qtWindow->activateWindow(); +*/ } static void ui_window_qt_set_visible(void *data, bool set_visible) { + (void)data; + (void)set_visible; /* TODO/FIXME */ } static void ui_window_qt_set_title(void *data, char *buf) { + (void)data; + (void)buf; +/* ui_window_qt_t *window = (ui_window_qt_t*)data; window->qtWindow->setWindowTitle(QString::fromUtf8(buf)); +*/ } static void ui_window_qt_set_droppable(void *data, bool droppable) { + (void)data; + (void)droppable; +/* ui_window_qt_t *window = (ui_window_qt_t*)data; - window->qtWindow->setAcceptDrops(droppable); + window->qtWindow->setAcceptDrops(droppable); +*/ } static bool ui_window_qt_focused(void *data) { + (void)data; +/* ui_window_qt_t *window = (ui_window_qt_t*)data; return window->qtWindow->isActiveWindow() && !window->qtWindow->isMinimized(); +*/ + return true; } -const ui_window_t ui_window_qt = { +ui_window_t ui_window_qt = { + ui_window_qt_init, ui_window_qt_destroy, ui_window_qt_set_focused, ui_window_qt_set_visible, diff --git a/ui/drivers/qt/wimp-test/main.cpp b/ui/drivers/qt/wimp-test/main.cpp deleted file mode 100644 index 7f0f8f6217..0000000000 --- a/ui/drivers/qt/wimp-test/main.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include -#include "../wrapper/wrapper.h" -#include "../wimp/wimp.h" - -#include -#include -#include - -struct Wimp* t; - - -int i=0; - -void *initGui(void *arg) -{ - char **arguments = (char**)arg; - t = ctrWimp(i,arguments); - CreateMainWindow(t); //-->uncomment this to open the Qt GUI - return 0; -} - -int main(int argc, char *argv[]) -{ - i = argc; - - pthread_t gui; - int rc; - rc=pthread_create(&gui, NULL, initGui, (void *)argv); - if(rc!=0) - { - printf("failed"); - exit(1); - } - - for(int j=0;j<100;j++) - { - Sleep(1000); - printf("test = %d\n",i); - i++; - } - - pthread_join(gui,NULL); - return 0; -} - diff --git a/ui/drivers/qt/wimp-test/wimp-test.pro b/ui/drivers/qt/wimp-test/wimp-test.pro deleted file mode 100644 index cd3966adf0..0000000000 --- a/ui/drivers/qt/wimp-test/wimp-test.pro +++ /dev/null @@ -1,33 +0,0 @@ -TARGET = wimp-test -CONFIG += console - -SOURCES += main.cpp - -Release:DESTDIR = ../build/release -Release:OBJECTS_DIR = ../build/release/obj -Release:MOC_DIR = ../build/release/moc -Release:RCC_DIR = ../build/release/rcc -Release:UI_DIR = ../build/release/ui - -Debug:DESTDIR = ../build/debug -Debug:OBJECTS_DIR = ../build/debug/obj -Debug:MOC_DIR = ../build/debug/moc -Debug:RCC_DIR = ../build/debug/rcc -Debug:UI_DIR = ../build/debug/ui - -win32:CONFIG(release, debug|release): LIBS += -L$$PWD/../build/release/ -lwimp.dll -else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/build/debug/ -lwimp.dll -else:unix: LIBS += -L$$PWD/../wimp/build/ -lwimp.dll - -win32:CONFIG(release, debug|release): LIBS += -L$$PWD/../build/release/ -lwrapper.dll -else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/../build/debug/ -lwrapper.dll -else:unix:CONFIG(debug, debug|release): LIBS += -L$$PWD/../build/debug/ -lwrapper.so -else:unix:CONFIG(release, debug|release): LIBS += -L$$PWD/../build/debug/ -lwrapper.so - -INCLUDEPATH += $$PWD/../../../../ -INCLUDEPATH += $$PWD/../../../../libretro-common/include/ - -win32:CONFIG(release, debug|release): INCLUDEPATH += $$PWD/../build/release -else:win32:CONFIG(debug, debug|release): INCLUDEPATH += $$PWD/../build/debug -else:unix:CONFIG(debug, debug|release): INCLUDEPATH += $$PWD/../build/release -else:unix:CONFIG(release, debug|release): INCLUDEPATH += $$PWD/../build/release diff --git a/ui/drivers/qt/wimp/main.qml b/ui/drivers/qt/wimp/main.qml deleted file mode 100644 index d917ed83ff..0000000000 --- a/ui/drivers/qt/wimp/main.qml +++ /dev/null @@ -1,163 +0,0 @@ -import QtQuick 2.2 -import Material 0.1 -import Material.ListItems 0.1 as ListItem - -ApplicationWindow { - id: mainWindow - title: "RetroArch" - width: 1280 - height: 720 - color: "#211822" - visible: true - theme { - primaryColor: Palette.colors["blue"]["500"] - primaryDarkColor: Palette.colors["blue"]["700"] - accentColor: Palette.colors["red"]["A200"] - tabHighlightColor: "white" - } - - /* temporary top level folder list */ - property var folders: [ - "C:\\", "D:\\" - ] - - property var sections: [ collections, cores, folders ] - property var sectionTitles: [ "Collections", "Cores", "File Browser" ] - - property string selectedItem: collections[0] - - - initialPage: Page { - id: page - - title: "Qt!" - - tabs: navDrawer.enabled ? [] : sectionTitles - - actionBar.maxActionCount: navDrawer.enabled ? 3 : 4 - - actions: [ - Action { - iconName: "action/search" - name: "Search" - enabled: true - }, - - Action { - iconName: "image/color_lens" - name: "Colors" - onTriggered: colorPicker.show() - }, - - Action { - iconName: "action/settings" - name: "Settings" - hoverAnimation: true - }, - - - Action { - iconName: "action/language" - name: "Language" - enabled: true - }, - - Action { - iconName: "action/account_circle" - name: "Accounts" - } - ] - - backAction: navDrawer.action - - NavigationDrawer { - id: navDrawer - enabled: page.width < Units.dp(500) - - - Flickable { - anchors.fill: parent - - contentHeight: Math.max(content.implicitHeight, height) - - Column { - id: content - anchors.fill: parent - - Repeater { - model: sections - - delegate: ListItem.Standard { - width: parent.width - ListItem.Subheader { - text: sectionTitles[index] - } - } - } - } - } - } - - - } - - Dialog { - id: colorPicker - title: "Pick color" - - positiveButtonText: "Done" - - MenuField { - id: selection - model: ["Primary color", "Accent color", "Background color"] - width: Units.dp(160) - } - - Grid { - columns: 7 - spacing: Units.dp(8) - - Repeater { - model: [ - "red", "pink", "purple", "deepPurple", "indigo", - "blue", "lightBlue", "cyan", "teal", "green", - "lightGreen", "lime", "yellow", "amber", "orange", - "deepOrange", "grey", "blueGrey", "brown", "black", - "white" - ] - - Rectangle { - width: Units.dp(30) - height: Units.dp(30) - radius: Units.dp(2) - color: Palette.colors[modelData]["500"] - border.width: modelData === "white" ? Units.dp(2) : 0 - border.color: Theme.alpha("#000", 0.26) - - Ink { - anchors.fill: parent - - onPressed: { - switch(selection.selectedIndex) { - case 0: - theme.primaryColor = parent.color - break; - case 1: - theme.accentColor = parent.color - break; - case 2: - theme.backgroundColor = parent.color - break; - } - } - } - } - } - } - - onRejected: { - // TODO set default colors again but we currently don't know what that is - } - } - -} diff --git a/ui/drivers/qt/wimp/qml.qrc b/ui/drivers/qt/wimp/qml.qrc deleted file mode 100644 index 219fc5bcb4..0000000000 --- a/ui/drivers/qt/wimp/qml.qrc +++ /dev/null @@ -1,5 +0,0 @@ - - - main.qml - - diff --git a/ui/drivers/qt/wimp/wimp.cpp b/ui/drivers/qt/wimp/wimp.cpp deleted file mode 100644 index 7c65bb14c1..0000000000 --- a/ui/drivers/qt/wimp/wimp.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* RetroArch - A frontend for libretro. - * Copyright (C) 2011-2017 - Andres Suarez - * - * RetroArch 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * RetroArch 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 RetroArch. - * If not, see . - */ - -#include "wimp.h" -#include "stdio.h" -#include -#include -#include -#include -#include -#include - -#include "../../../../file_path_special.h" - -QObject *topLevel; - -int Wimp::CreateMainWindow() -{ - - engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); - topLevel = engine.rootObjects().value(0); - window = qobject_cast(topLevel); - - collections.append("Collection dir not defined"); - - engine.rootContext()->setContextProperty("collections", QVariant::fromValue(collections)); - - cores.append("Core dir not defined"); - - engine.rootContext()->setContextProperty("cores", QVariant::fromValue(cores)); - - return this->exec(); - -} - -void Wimp::GetSettings(settings_t *s) -{ - settings = s; -} diff --git a/ui/drivers/qt/wimp/wimp.h b/ui/drivers/qt/wimp/wimp.h deleted file mode 100644 index e456307592..0000000000 --- a/ui/drivers/qt/wimp/wimp.h +++ /dev/null @@ -1,63 +0,0 @@ -/* RetroArch - A frontend for libretro. - * Copyright (C) 2011-2017 - Andrés Suárez - * - * RetroArch 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * RetroArch 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 RetroArch. - * If not, see . - */ - -#ifndef WIMP_H -#define WIMP_H - -/* this is the only define missing from config.h remove these once - * we can build everything with a single makefile - */ - -#ifndef HAVE_MENU -#define HAVE_MENU -#endif - -#include "config.h" -#include "configuration.h" - -#include "wimp_global.h" -#include -#include -#include -#include - -class WIMPSHARED_EXPORT Wimp : public QGuiApplication -{ - QQuickWindow *window; - - Q_OBJECT - public: - Wimp(int argc, char *argv[]): QGuiApplication(argc, argv) {} - - /* create the main Qt window */ - int CreateMainWindow(); - - /* get a pointer to RetroArch settings */ - void GetSettings(settings_t *s); - - void GetCollections(char* path); - void GetCores(char* path); - - private: - /* pointer to RetroArch settings */ - settings_t *settings; - QStringList collections; - QStringList cores; - QQmlApplicationEngine engine; - -}; - -#endif // WIMP_H - diff --git a/ui/drivers/qt/wimp/wimp.pro b/ui/drivers/qt/wimp/wimp.pro deleted file mode 100644 index 4917f2c1ea..0000000000 --- a/ui/drivers/qt/wimp/wimp.pro +++ /dev/null @@ -1,37 +0,0 @@ -#------------------------------------------------- -# -# Project created by QtCreator 2014-10-21T21:15:44 -# -#------------------------------------------------- - -QT += qml quick widgets - -TARGET = wimp -TEMPLATE = lib - -DEFINES += WIMP_LIBRARY - -SOURCES += \ - wimp.cpp - -HEADERS +=\ - wimp.h \ - wimp_global.h - -RESOURCES += \ - qml.qrc - -Release:DESTDIR = ../build/release -Release:OBJECTS_DIR = ../build/release/obj -Release:MOC_DIR = ../build/release/moc -Release:RCC_DIR = ../build/release/rcc -Release:UI_DIR = ../build/release/ui - -Debug:DESTDIR = ../build/debug -Debug:OBJECTS_DIR = ../build/debug/obj -Debug:MOC_DIR = ../build/debug/moc -Debug:RCC_DIR = ../build/debug/rcc -Debug:UI_DIR = ../build/debug/ui - -INCLUDEPATH += $$PWD/../../../../ -INCLUDEPATH += $$PWD/../../../../libretro-common/include/ diff --git a/ui/drivers/qt/wimp/wimp_global.h b/ui/drivers/qt/wimp/wimp_global.h deleted file mode 100644 index fbad654773..0000000000 --- a/ui/drivers/qt/wimp/wimp_global.h +++ /dev/null @@ -1,28 +0,0 @@ -/* RetroArch - A frontend for libretro. - * Copyright (C) 2011-2017 - Andrés Suárez - * - * RetroArch 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * RetroArch 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 RetroArch. - * If not, see . - */ - - -#ifndef WIMP_GLOBAL_H -#define WIMP_GLOBAL_H - -#include - -#if defined(WIMP_LIBRARY) -# define WIMPSHARED_EXPORT Q_DECL_EXPORT -#else -# define WIMPSHARED_EXPORT Q_DECL_IMPORT -#endif - -#endif // WIMP_GLOBAL_H diff --git a/ui/drivers/qt/wrapper/deployment.pri b/ui/drivers/qt/wrapper/deployment.pri deleted file mode 100644 index 4642d1b5ac..0000000000 --- a/ui/drivers/qt/wrapper/deployment.pri +++ /dev/null @@ -1,191 +0,0 @@ -# This file was generated by an application wizard of Qt Creator. -# The code below handles deployment to Android and Maemo, aswell as copying -# of the application data to shadow build directories on desktop. -# It is recommended not to modify this file, since newer versions of Qt Creator -# may offer an updated version of it. - -defineTest(qtcAddDeployment) { -for(deploymentfolder, DEPLOYMENTFOLDERS) { - item = item$${deploymentfolder} - greaterThan(QT_MAJOR_VERSION, 4) { - itemsources = $${item}.files - } else { - itemsources = $${item}.sources - } - $$itemsources = $$eval($${deploymentfolder}.source) - itempath = $${item}.path - $$itempath= $$eval($${deploymentfolder}.target) - export($$itemsources) - export($$itempath) - DEPLOYMENT += $$item -} - -MAINPROFILEPWD = $$PWD - -android-no-sdk { - for(deploymentfolder, DEPLOYMENTFOLDERS) { - item = item$${deploymentfolder} - itemfiles = $${item}.files - $$itemfiles = $$eval($${deploymentfolder}.source) - itempath = $${item}.path - $$itempath = /data/user/qt/$$eval($${deploymentfolder}.target) - export($$itemfiles) - export($$itempath) - INSTALLS += $$item - } - - target.path = /data/user/qt - - export(target.path) - INSTALLS += target -} else:android { - for(deploymentfolder, DEPLOYMENTFOLDERS) { - item = item$${deploymentfolder} - itemfiles = $${item}.files - $$itemfiles = $$eval($${deploymentfolder}.source) - itempath = $${item}.path - $$itempath = /assets/$$eval($${deploymentfolder}.target) - export($$itemfiles) - export($$itempath) - INSTALLS += $$item - } - - x86 { - target.path = /libs/x86 - } else: armeabi-v7a { - target.path = /libs/armeabi-v7a - } else { - target.path = /libs/armeabi - } - - export(target.path) - INSTALLS += target -} else:win32 { - copyCommand = - for(deploymentfolder, DEPLOYMENTFOLDERS) { - source = $$MAINPROFILEPWD/$$eval($${deploymentfolder}.source) - source = $$replace(source, /, \\) - sourcePathSegments = $$split(source, \\) - target = $$OUT_PWD/$$eval($${deploymentfolder}.target)/$$last(sourcePathSegments) - target = $$replace(target, /, \\) - target ~= s,\\\\\\.?\\\\,\\, - !isEqual(source,$$target) { - !isEmpty(copyCommand):copyCommand += && - isEqual(QMAKE_DIR_SEP, \\) { - copyCommand += $(COPY_DIR) \"$$source\" \"$$target\" - } else { - source = $$replace(source, \\\\, /) - target = $$OUT_PWD/$$eval($${deploymentfolder}.target) - target = $$replace(target, \\\\, /) - copyCommand += test -d \"$$target\" || mkdir -p \"$$target\" && cp -r \"$$source\" \"$$target\" - } - } - } - !isEmpty(copyCommand) { - copyCommand = @echo Copying application data... && $$copyCommand - copydeploymentfolders.commands = $$copyCommand - first.depends = $(first) copydeploymentfolders - export(first.depends) - export(copydeploymentfolders.commands) - QMAKE_EXTRA_TARGETS += first copydeploymentfolders - } -} else:ios { - copyCommand = - for(deploymentfolder, DEPLOYMENTFOLDERS) { - source = $$MAINPROFILEPWD/$$eval($${deploymentfolder}.source) - source = $$replace(source, \\\\, /) - target = $CODESIGNING_FOLDER_PATH/$$eval($${deploymentfolder}.target) - target = $$replace(target, \\\\, /) - sourcePathSegments = $$split(source, /) - targetFullPath = $$target/$$last(sourcePathSegments) - targetFullPath ~= s,/\\.?/,/, - !isEqual(source,$$targetFullPath) { - !isEmpty(copyCommand):copyCommand += && - copyCommand += mkdir -p \"$$target\" - copyCommand += && cp -r \"$$source\" \"$$target\" - } - } - !isEmpty(copyCommand) { - copyCommand = echo Copying application data... && $$copyCommand - !isEmpty(QMAKE_POST_LINK): QMAKE_POST_LINK += ";" - QMAKE_POST_LINK += "$$copyCommand" - export(QMAKE_POST_LINK) - } -} else:unix { - maemo5 { - desktopfile.files = $${TARGET}.desktop - desktopfile.path = /usr/share/applications/hildon - icon.files = $${TARGET}64.png - icon.path = /usr/share/icons/hicolor/64x64/apps - } else:!isEmpty(MEEGO_VERSION_MAJOR) { - desktopfile.files = $${TARGET}_harmattan.desktop - desktopfile.path = /usr/share/applications - icon.files = $${TARGET}80.png - icon.path = /usr/share/icons/hicolor/80x80/apps - } else { # Assumed to be a Desktop Unix - copyCommand = - for(deploymentfolder, DEPLOYMENTFOLDERS) { - source = $$MAINPROFILEPWD/$$eval($${deploymentfolder}.source) - source = $$replace(source, \\\\, /) - macx { - target = $$OUT_PWD/$${TARGET}.app/Contents/Resources/$$eval($${deploymentfolder}.target) - } else { - target = $$OUT_PWD/$$eval($${deploymentfolder}.target) - } - target = $$replace(target, \\\\, /) - sourcePathSegments = $$split(source, /) - targetFullPath = $$target/$$last(sourcePathSegments) - targetFullPath ~= s,/\\.?/,/, - !isEqual(source,$$targetFullPath) { - !isEmpty(copyCommand):copyCommand += && - copyCommand += $(MKDIR) \"$$target\" - copyCommand += && $(COPY_DIR) \"$$source\" \"$$target\" - } - } - !isEmpty(copyCommand) { - copyCommand = @echo Copying application data... && $$copyCommand - copydeploymentfolders.commands = $$copyCommand - first.depends = $(first) copydeploymentfolders - export(first.depends) - export(copydeploymentfolders.commands) - QMAKE_EXTRA_TARGETS += first copydeploymentfolders - } - } - !isEmpty(target.path) { - installPrefix = $${target.path} - } else { - installPrefix = /opt/$${TARGET} - } - for(deploymentfolder, DEPLOYMENTFOLDERS) { - item = item$${deploymentfolder} - itemfiles = $${item}.files - $$itemfiles = $$eval($${deploymentfolder}.source) - itempath = $${item}.path - $$itempath = $${installPrefix}/$$eval($${deploymentfolder}.target) - export($$itemfiles) - export($$itempath) - INSTALLS += $$item - } - - !isEmpty(desktopfile.path) { - export(icon.files) - export(icon.path) - export(desktopfile.files) - export(desktopfile.path) - INSTALLS += icon desktopfile - } - - isEmpty(target.path) { - target.path = $${installPrefix}/bin - export(target.path) - } - INSTALLS += target -} - -export (ICON) -export (INSTALLS) -export (DEPLOYMENT) -export (LIBS) -export (QMAKE_EXTRA_TARGETS) -} - diff --git a/ui/drivers/qt/wrapper/wrapper.cpp b/ui/drivers/qt/wrapper/wrapper.cpp deleted file mode 100644 index 8781b0402f..0000000000 --- a/ui/drivers/qt/wrapper/wrapper.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* RetroArch - A frontend for libretro. - * Copyright (C) 2011-2017 - Andrés Suárez - * - * RetroArch 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * RetroArch 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 RetroArch. - * If not, see . - */ - - -#include "../wimp/wimp.h" -#include "../wimp/wimp_global.h" -#include "wrapper.h" - - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct Wimp Wimp; - -Wimp* ctrWimp(int argc, char *argv[]){ - return new Wimp(argc,argv); -} - -int CreateMainWindow(Wimp* p) -{ - return p->CreateMainWindow(); -} - -void GetSettings(Wimp* p, settings_t *s) -{ - return p->GetSettings(s); -} - -#ifdef __cplusplus -} -#endif diff --git a/ui/drivers/qt/wrapper/wrapper.h b/ui/drivers/qt/wrapper/wrapper.h deleted file mode 100644 index 6a0ad757c0..0000000000 --- a/ui/drivers/qt/wrapper/wrapper.h +++ /dev/null @@ -1,38 +0,0 @@ -/* RetroArch - A frontend for libretro. - * Copyright (C) 2011-2017 - Andrés Suárez - * - * RetroArch 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * RetroArch 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 RetroArch. - * If not, see . - */ - -#ifndef HAVE_MENU -#define HAVE_MENU -#endif - -#include "config.h" -#include "configuration.h" - -#ifndef WRAPPER_H -#define WRAPPER_H - -RETRO_BEGIN_DECLS - -typedef struct Wimp Wimp; -typedef settings_t (*config_get_ptr_cb); - -Wimp* ctrWimp(int argc, char *argv[]); - -int CreateMainWindow(Wimp* p); -void GetSettings(Wimp* p, settings_t *s); - -RETRO_END_DECLS - -#endif // WRAPPER_H diff --git a/ui/drivers/qt/wrapper/wrapper.pro b/ui/drivers/qt/wrapper/wrapper.pro deleted file mode 100644 index cc4b69c9a8..0000000000 --- a/ui/drivers/qt/wrapper/wrapper.pro +++ /dev/null @@ -1,40 +0,0 @@ -TEMPLATE = lib -CONFIG += console -CONFIG -= app_bundle -#CONFIG -= qt -QT += qml quick widgets - -SOURCES += \ - wrapper.cpp - -include(deployment.pri) -qtcAddDeployment() - -HEADERS += \ - wrapper.h - - -Release:DESTDIR = ../build/release -Release:OBJECTS_DIR = ../build/release/obj -Release:MOC_DIR = ../build/release/moc -Release:RCC_DIR = ../build/release/rcc -Release:UI_DIR = ../build/release/ui - -Debug:DESTDIR = ../build/debug -Debug:OBJECTS_DIR = ../build/debug/obj -Debug:MOC_DIR = ../build/debug/moc -Debug:RCC_DIR = ../build/debug/rcc -Debug:UI_DIR = ../build/debug/ui - -win32:CONFIG(release, debug|release): LIBS += -L$$PWD/../build/release/ -lwimp.dll -else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/../build/debug/ -lwimp.dll -else:unix:CONFIG(debug, debug|release): LIBS += -L$$PWD/../build/debug/ -lwimp.so -else:unix:CONFIG(release, debug|release): LIBS += -L$$PWD/../build/debug/ -lwimp.so - -win32:CONFIG(release, debug|release): INCLUDEPATH += $$PWD/../build/release -else:win32:CONFIG(debug, debug|release): INCLUDEPATH += $$PWD/../build/debug -else:unix:CONFIG(debug, debug|release): INCLUDEPATH += $$PWD/../build/release -else:unix:CONFIG(release, debug|release): INCLUDEPATH += $$PWD/../build/release - -INCLUDEPATH += $$PWD/../../../../ -INCLUDEPATH += $$PWD/../../../../libretro-common/include/ diff --git a/ui/drivers/ui_cocoa.m b/ui/drivers/ui_cocoa.m index df87c017d4..d790dda58e 100644 --- a/ui/drivers/ui_cocoa.m +++ b/ui/drivers/ui_cocoa.m @@ -197,13 +197,13 @@ static char** waiting_argv; SEL selector = NSSelectorFromString(BOXSTRING("setCollectionBehavior:")); SEL fsselector = NSSelectorFromString(BOXSTRING("toggleFullScreen:")); apple_platform = self; - + if ([self.window respondsToSelector:selector]) { if ([self.window respondsToSelector:fsselector]) [self.window setCollectionBehavior:NS_WINDOW_COLLECTION_BEHAVIOR_FULLSCREEN_PRIMARY]; } - + [self.window setAcceptsMouseMovedEvents: YES]; [[CocoaView get] setFrame: [[self.window contentView] bounds]]; @@ -245,7 +245,7 @@ static char** waiting_argv; if (ret == -1) break; }while(1); - + main_exit(NULL); } @@ -286,7 +286,7 @@ static char** waiting_argv; if (system) core_name = system->library_name; - + if (core_name) { content_ctx_info_t content_info = {0}; @@ -329,10 +329,10 @@ static void open_core_handler(ui_browser_window_state_t *state, bool result) return; settings_t *settings = config_get_ptr(); - + rarch_ctl(RARCH_CTL_SET_LIBRETRO_PATH, (void*)state->result); ui_companion_event_command(CMD_EVENT_LOAD_CORE); - + if (info && info->load_no_content && settings->bools.set_supports_no_game_enable) { @@ -354,16 +354,16 @@ static void open_document_handler(ui_browser_window_state_t *state, bool result) return; if (!result) return; - + rarch_system_info_t *info = runloop_get_system_info(); struct retro_system_info *system = &info->info; const char *core_name = NULL; - + if (system) core_name = system->library_name; - + path_set(RARCH_PATH_CONTENT, state->result); - + if (core_name) { content_ctx_info_t content_info = {0}; @@ -377,20 +377,20 @@ static void open_document_handler(ui_browser_window_state_t *state, bool result) - (IBAction)openCore:(id)sender { const ui_browser_window_t *browser = ui_companion_driver_get_browser_window_ptr(); - + if (browser) { ui_browser_window_state_t browser_state; settings_t *settings = config_get_ptr(); - + browser_state.filters = strdup("dylib"); browser_state.filters_title = strdup("Core"); browser_state.title = strdup("Load Core"); browser_state.startdir = strdup(settings->paths.directory_libretro); - + bool result = browser->open(&browser_state); open_core_handler(&browser_state, result); - + free(browser_state.filters); free(browser_state.filters_title); free(browser_state.title); @@ -401,22 +401,22 @@ static void open_document_handler(ui_browser_window_state_t *state, bool result) - (void)openDocument:(id)sender { const ui_browser_window_t *browser = ui_companion_driver_get_browser_window_ptr(); - + if (browser) { ui_browser_window_state_t browser_state = {{0}}; settings_t *settings = config_get_ptr(); NSString *startdir = BOXSTRING(settings->paths.directory_menu_content); - + if (!startdir.length) startdir = BOXSTRING("/"); - + browser_state.title = strdup("Load Content"); browser_state.startdir = strdup([startdir UTF8String]); - + bool result = browser->open(&browser_state); open_document_handler(&browser_state, result); - + free(browser_state.startdir); free(browser_state.title); } @@ -440,7 +440,7 @@ static void open_document_handler(ui_browser_window_state_t *state, bool result) { enum event_command cmd; unsigned sender_tag = (unsigned)[sender tag]; - + switch (sender_tag) { case 1: @@ -477,7 +477,7 @@ static void open_document_handler(ui_browser_window_state_t *state, bool result) cmd = CMD_EVENT_NONE; break; } - + if (sender_tag >= 10 && sender_tag <= 19) { unsigned idx = (sender_tag - (10-1)); @@ -503,7 +503,7 @@ int main(int argc, char *argv[]) if (!strncmp(argv[1], "-psn", 4)) argc = 1; } - + waiting_argc = argc; waiting_argv = argv; @@ -520,9 +520,10 @@ static void ui_companion_cocoa_notify_content_loaded(void *data) (void)data; } -static void ui_companion_cocoa_toggle(void *data) +static void ui_companion_cocoa_toggle(void *data, bool force) { (void)data; + (void)force; } static int ui_companion_cocoa_iterate(void *data, unsigned action) @@ -571,7 +572,7 @@ static void *ui_companion_cocoa_get_main_window(void *data) return ((RetroArch_OSX*)[[NSApplication sharedApplication] delegate]).window; } -const ui_companion_driver_t ui_companion_cocoa = { +ui_companion_driver_t ui_companion_cocoa = { ui_companion_cocoa_init, ui_companion_cocoa_deinit, ui_companion_cocoa_iterate, @@ -583,6 +584,7 @@ const ui_companion_driver_t ui_companion_cocoa = { NULL, NULL, ui_companion_cocoa_get_main_window, + NULL, &ui_browser_window_cocoa, &ui_msg_window_cocoa, &ui_window_cocoa, diff --git a/ui/drivers/ui_cocoatouch.m b/ui/drivers/ui_cocoatouch.m index 481ff7f827..6ae8fa5cae 100644 --- a/ui/drivers/ui_cocoatouch.m +++ b/ui/drivers/ui_cocoatouch.m @@ -698,7 +698,7 @@ static void ui_companion_cocoatouch_msg_queue_push(const char *msg, } } -const ui_companion_driver_t ui_companion_cocoatouch = { +ui_companion_driver_t ui_companion_cocoatouch = { ui_companion_cocoatouch_init, ui_companion_cocoatouch_deinit, ui_companion_cocoatouch_iterate, diff --git a/ui/drivers/ui_null.c b/ui/drivers/ui_null.c index 833bb73887..6e901a63bd 100644 --- a/ui/drivers/ui_null.c +++ b/ui/drivers/ui_null.c @@ -58,9 +58,10 @@ static void ui_companion_null_notify_content_loaded(void *data) (void)data; } -static void ui_companion_null_toggle(void *data) +static void ui_companion_null_toggle(void *data, bool force) { (void)data; + (void)force; } static void ui_companion_null_event_command(void *data, enum event_command cmd) @@ -77,7 +78,7 @@ static void ui_companion_null_notify_list_pushed(void *data, (void)menu_list; } -const ui_companion_driver_t ui_companion_null = { +ui_companion_driver_t ui_companion_null = { ui_companion_null_init, ui_companion_null_deinit, ui_companion_null_iterate, @@ -89,6 +90,7 @@ const ui_companion_driver_t ui_companion_null = { NULL, NULL, NULL, + NULL, &ui_browser_window_null, &ui_msg_window_null, &ui_window_null, diff --git a/ui/drivers/ui_qt.cpp b/ui/drivers/ui_qt.cpp index 7f71aa121e..3936af5719 100644 --- a/ui/drivers/ui_qt.cpp +++ b/ui/drivers/ui_qt.cpp @@ -1,5 +1,6 @@ /* RetroArch - A frontend for libretro. * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2018 - Brad Parker * * RetroArch 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 Found- @@ -13,59 +14,168 @@ * If not, see . */ -#include -#include -#include -#include -#include - +extern "C" { #include -#include #ifdef HAVE_CONFIG_H #include "../../config.h" #endif -#ifdef HAVE_QT_WRAPPER -#include "qt/wrapper/wrapper.h" -#else -#include "ui_qt.h" -#endif - #include "../ui_companion_driver.h" #include "../../core.h" #include "../../configuration.h" #include "../../retroarch.h" +#include "../../verbosity.h" +#include "../../msg_hash.h" #include "../../tasks/tasks_internal.h" +} -#ifdef HAVE_QT_WRAPPER -struct Wimp* wimp; -char* args[] = {""}; +#include "ui_qt.h" + +#include +#include +#include +#include +#include + +#define INITIAL_WIDTH 1280 +#define INITIAL_HEIGHT 720 + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) +#define GROUPED_DRAGGING QMainWindow::GroupedDragging +#else +#define GROUPED_DRAGGING static_cast(0) #endif +static bool already_started = false; + typedef struct ui_companion_qt { - void *empty; -#ifdef HAVE_QT_WRAPPER - volatile bool quit; - slock_t *lock; - sthread_t *thread; -#endif + ui_application_qt_t *app; + ui_window_qt_t *window; } ui_companion_qt_t; -#ifdef HAVE_QT_WRAPPER -static void qt_thread(void *data) +ThumbnailWidget::ThumbnailWidget(QWidget *parent) : + QWidget(parent) { - ui_companion_qt_t *handle = (ui_companion_qt_t*)data; - wimp = ctrWimp(0, NULL); - if(wimp) - { - settings_t *settings = config_get_ptr(); - GetSettings(wimp, settings); - CreateMainWindow(wimp); - } } -#endif + +void ThumbnailWidget::paintEvent(QPaintEvent *event) +{ + QStyleOption o; + QPainter p; + o.initFrom(this); + p.begin(this); + style()->drawPrimitive( + QStyle::PE_Widget, &o, &p, this); + p.end(); + + QWidget::paintEvent(event); +} + +void ThumbnailWidget::resizeEvent(QResizeEvent *event) +{ + QWidget::resizeEvent(event); +} + +QSize ThumbnailWidget::sizeHint() const +{ + return QSize(256, 256); +} + +ThumbnailLabel::ThumbnailLabel(QWidget *parent) : + QWidget(parent) + ,m_pixmap(NULL) + ,m_pixmapWidth(0) + ,m_pixmapHeight(0) +{ +} + +ThumbnailLabel::~ThumbnailLabel() +{ + if (m_pixmap) + delete m_pixmap; +} + +void ThumbnailLabel::setPixmap(const QPixmap &pixmap) +{ + m_pixmapWidth = pixmap.width(); + m_pixmapHeight = pixmap.height(); + + if (m_pixmap) + delete m_pixmap; + + m_pixmap = new QPixmap(pixmap); +} + +QSize ThumbnailLabel::sizeHint() const +{ + return QSize(256, 256); +} + +void ThumbnailLabel::paintEvent(QPaintEvent *event) +{ + int w = width(); + int h = height(); + QStyleOption o; + QPainter p; + + event->accept(); + + o.initFrom(this); + p.begin(this); + style()->drawPrimitive( + QStyle::PE_Widget, &o, &p, this); + p.end(); + + if (!m_pixmap || m_pixmap->isNull()) + { + m_pixmap = new QPixmap(sizeHint()); + m_pixmap->fill(QColor(0, 0, 0, 0)); + } + + if (w > 0 && h > 0 && m_pixmap && !m_pixmap->isNull()) + { + int newHeight = (m_pixmap->height() / static_cast(m_pixmap->width())) * width(); + QPixmap pixmapScaled = *m_pixmap; + QPixmap pixmap; + QPainter pScale; + int pw = 0; + int ph = 0; + unsigned *buf = new unsigned[w * h]; + + if (newHeight > h) + pixmapScaled = pixmapScaled.scaledToHeight(h, Qt::SmoothTransformation); + else + pixmapScaled = pixmapScaled.scaledToWidth(w, Qt::SmoothTransformation); + + pw = pixmapScaled.width(); + ph = pixmapScaled.height(); + + pixmap = QPixmap(w, h); + pixmap.fill(QColor(0, 0, 0, 0)); + + pScale.begin(&pixmap); + pScale.drawPixmap(QRect((w - pw) / 2, (h - ph) / 2, pw, ph), pixmapScaled, pixmapScaled.rect()); + pScale.end(); + + if (!pixmap.isNull()) + { + p.begin(this); + p.drawPixmap(rect(), pixmap, pixmap.rect()); + p.end(); + } + + delete []buf; + } + else + QWidget::paintEvent(event); +} + +void ThumbnailLabel::resizeEvent(QResizeEvent *event) +{ + QWidget::resizeEvent(event); +} static void ui_companion_qt_deinit(void *data) { @@ -74,32 +184,305 @@ static void ui_companion_qt_deinit(void *data) if (!handle) return; -#ifdef HAVE_QT_WRAPPER - slock_free(handle->lock); - sthread_join(handle->thread); -#endif + /* why won't deleteLater() here call the destructor? */ + delete handle->window->qtWindow; free(handle); } -static void *ui_companion_qt_init(void) +static void* ui_companion_qt_init(void) { ui_companion_qt_t *handle = (ui_companion_qt_t*)calloc(1, sizeof(*handle)); + MainWindow *mainwindow = NULL; + QHBoxLayout *browserButtonsHBoxLayout = NULL; + QVBoxLayout *layout = NULL; + QVBoxLayout *launchWithWidgetLayout = NULL; + QHBoxLayout *coreComboBoxLayout = NULL; + QMenuBar *menu = NULL; + QDesktopWidget *desktop = NULL; + QMenu *fileMenu = NULL; + QMenu *editMenu = NULL; + QMenu *viewMenu = NULL; + QMenu *viewClosedDocksMenu = NULL; + QRect desktopRect; + QDockWidget *thumbnailDock = NULL; + QDockWidget *thumbnail2Dock = NULL; + QDockWidget *thumbnail3Dock = NULL; + QDockWidget *browserAndPlaylistTabDock = NULL; + QDockWidget *coreSelectionDock = NULL; + QTabWidget *browserAndPlaylistTabWidget = NULL; + QWidget *widget = NULL; + QWidget *browserWidget = NULL; + QWidget *playlistWidget = NULL; + QWidget *coreSelectionWidget = NULL; + QWidget *launchWithWidget = NULL; + ThumbnailWidget *thumbnailWidget = NULL; + ThumbnailWidget *thumbnail2Widget = NULL; + ThumbnailWidget *thumbnail3Widget = NULL; + QPushButton *browserDownloadsButton = NULL; + QPushButton *browserUpButton = NULL; + QPushButton *browserStartButton = NULL; + ThumbnailLabel *thumbnail = NULL; + ThumbnailLabel *thumbnail2 = NULL; + ThumbnailLabel *thumbnail3 = NULL; + QAction *editSearchAction = NULL; + QAction *loadCoreAction = NULL; + QAction *unloadCoreAction = NULL; + QAction *exitAction = NULL; + QComboBox *launchWithComboBox = NULL; + QSettings *qsettings = NULL; + if (!handle) return NULL; -#ifdef HAVE_QT_WRAPPER - settings_t *settings = config_get_ptr(); - handle->lock = slock_new(); - handle->thread = sthread_create(qt_thread, handle); - if (!handle->thread) - { - slock_free(handle->lock); - free(handle); - return NULL; - } + handle->app = static_cast(ui_application_qt.initialize()); + handle->window = static_cast(ui_window_qt.init()); + + desktop = qApp->desktop(); + desktopRect = desktop->availableGeometry(); + + mainwindow = handle->window->qtWindow; + + qsettings = mainwindow->settings(); + + mainwindow->resize(qMin(desktopRect.width(), INITIAL_WIDTH), qMin(desktopRect.height(), INITIAL_HEIGHT)); + mainwindow->setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, mainwindow->size(), desktopRect)); + + mainwindow->setWindowTitle("RetroArch"); + mainwindow->setDockOptions(QMainWindow::AnimatedDocks | QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks | GROUPED_DRAGGING); + + widget = new QWidget(mainwindow); + widget->setObjectName("tableWidget"); + + layout = new QVBoxLayout(); + layout->addWidget(mainwindow->contentTableWidget()); + + widget->setLayout(layout); + + mainwindow->setCentralWidget(widget); + + menu = mainwindow->menuBar(); + + fileMenu = menu->addMenu(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_FILE)); + + loadCoreAction = fileMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_FILE_LOAD_CORE), mainwindow, SLOT(onLoadCoreClicked())); + loadCoreAction->setShortcut(QKeySequence("Ctrl+L")); + + unloadCoreAction = fileMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_FILE_UNLOAD_CORE), mainwindow, SLOT(onUnloadCoreMenuAction())); + unloadCoreAction->setObjectName("unloadCoreAction"); + unloadCoreAction->setEnabled(false); + unloadCoreAction->setShortcut(QKeySequence("Ctrl+U")); + + exitAction = fileMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_FILE_EXIT), mainwindow, SLOT(close())); + exitAction->setShortcut(QKeySequence::Quit); + + editMenu = menu->addMenu(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_EDIT)); + editSearchAction = editMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_EDIT_SEARCH), mainwindow->searchLineEdit(), SLOT(setFocus())); + editSearchAction->setShortcut(QKeySequence::Find); + + viewMenu = menu->addMenu(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW)); + viewClosedDocksMenu = viewMenu->addMenu(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_CLOSED_DOCKS)); + viewClosedDocksMenu->setObjectName("viewClosedDocksMenu"); + + QObject::connect(viewClosedDocksMenu, SIGNAL(aboutToShow()), mainwindow, SLOT(onViewClosedDocksAboutToShow())); + + viewMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS), mainwindow->viewOptionsDialog(), SLOT(showDialog())); + + playlistWidget = new QWidget(); + playlistWidget->setLayout(new QVBoxLayout()); + playlistWidget->setObjectName("playlistWidget"); + + playlistWidget->layout()->addWidget(mainwindow->playlistListWidget()); + + browserWidget = new QWidget(); + browserWidget->setLayout(new QVBoxLayout()); + browserWidget->setObjectName("browserWidget"); + + browserDownloadsButton = new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_ASSETS_DIRECTORY)); + browserUpButton = new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_TAB_FILE_BROWSER_UP)); + browserStartButton = new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_FAVORITES)); + + QObject::connect(browserDownloadsButton, SIGNAL(clicked()), mainwindow, SLOT(onBrowserDownloadsClicked())); + QObject::connect(browserUpButton, SIGNAL(clicked()), mainwindow, SLOT(onBrowserUpClicked())); + QObject::connect(browserStartButton, SIGNAL(clicked()), mainwindow, SLOT(onBrowserStartClicked())); + + browserButtonsHBoxLayout = new QHBoxLayout(); + browserButtonsHBoxLayout->addWidget(browserUpButton); + browserButtonsHBoxLayout->addWidget(browserStartButton); + browserButtonsHBoxLayout->addWidget(browserDownloadsButton); + + qobject_cast(browserWidget->layout())->addLayout(browserButtonsHBoxLayout); + browserWidget->layout()->addWidget(mainwindow->dirTreeView()); + + browserAndPlaylistTabWidget = mainwindow->browserAndPlaylistTabWidget(); + browserAndPlaylistTabWidget->setObjectName("browserAndPlaylistTabWidget"); + + /* Several functions depend on the same tab title strings here, so if you change these, make sure to change those too + * setCoreActions() + * onTabWidgetIndexChanged() + * onCurrentListItemChanged() + */ + browserAndPlaylistTabWidget->addTab(playlistWidget, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_TAB_PLAYLISTS)); + browserAndPlaylistTabWidget->addTab(browserWidget, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_TAB_FILE_BROWSER)); + + browserAndPlaylistTabDock = new QDockWidget(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_DOCK_CONTENT_BROWSER), mainwindow); + browserAndPlaylistTabDock->setObjectName("browserAndPlaylistTabDock"); + browserAndPlaylistTabDock->setProperty("default_area", Qt::LeftDockWidgetArea); + browserAndPlaylistTabDock->setProperty("menu_text", msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_DOCK_CONTENT_BROWSER)); + browserAndPlaylistTabDock->setWidget(browserAndPlaylistTabWidget); + + mainwindow->addDockWidget(static_cast(browserAndPlaylistTabDock->property("default_area").toInt()), browserAndPlaylistTabDock); + + browserButtonsHBoxLayout->addItem(new QSpacerItem(browserAndPlaylistTabWidget->tabBar()->width(), 20, QSizePolicy::Expanding, QSizePolicy::Minimum)); + + thumbnailWidget = new ThumbnailWidget(); + thumbnail2Widget = new ThumbnailWidget(); + thumbnail3Widget = new ThumbnailWidget(); + + thumbnailWidget->setLayout(new QVBoxLayout()); + thumbnail2Widget->setLayout(new QVBoxLayout()); + thumbnail3Widget->setLayout(new QVBoxLayout()); + + thumbnailWidget->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + thumbnail2Widget->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + thumbnail3Widget->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + + thumbnail = new ThumbnailLabel(); + thumbnail->setObjectName("thumbnail"); + + thumbnail2 = new ThumbnailLabel(); + thumbnail2->setObjectName("thumbnail2"); + + thumbnail3 = new ThumbnailLabel(); + thumbnail3->setObjectName("thumbnail3"); + + thumbnail->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + thumbnail2->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + thumbnail3->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + + QObject::connect(mainwindow, SIGNAL(thumbnailChanged(const QPixmap&)), thumbnail, SLOT(setPixmap(const QPixmap&))); + QObject::connect(mainwindow, SIGNAL(thumbnail2Changed(const QPixmap&)), thumbnail2, SLOT(setPixmap(const QPixmap&))); + QObject::connect(mainwindow, SIGNAL(thumbnail3Changed(const QPixmap&)), thumbnail3, SLOT(setPixmap(const QPixmap&))); + + thumbnailWidget->layout()->addWidget(thumbnail); + thumbnail2Widget->layout()->addWidget(thumbnail2); + thumbnail3Widget->layout()->addWidget(thumbnail3); + + thumbnailDock = new QDockWidget(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_BOXART), mainwindow); + thumbnailDock->setObjectName("thumbnailDock"); + thumbnailDock->setProperty("default_area", Qt::RightDockWidgetArea); + thumbnailDock->setProperty("menu_text", msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_BOXART)); + thumbnailDock->setWidget(thumbnailWidget); + + mainwindow->addDockWidget(static_cast(thumbnailDock->property("default_area").toInt()), thumbnailDock); + + thumbnail2Dock = new QDockWidget(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_TITLE_SCREEN), mainwindow); + thumbnail2Dock->setObjectName("thumbnail2Dock"); + thumbnail2Dock->setProperty("default_area", Qt::RightDockWidgetArea); + thumbnail2Dock->setProperty("menu_text", msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_TITLE_SCREEN)); + thumbnail2Dock->setWidget(thumbnail2Widget); + + mainwindow->addDockWidget(static_cast(thumbnail2Dock->property("default_area").toInt()), thumbnail2Dock); + + thumbnail3Dock = new QDockWidget(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_SCREENSHOT), mainwindow); + thumbnail3Dock->setObjectName("thumbnail3Dock"); + thumbnail3Dock->setProperty("default_area", Qt::RightDockWidgetArea); + thumbnail3Dock->setProperty("menu_text", msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_SCREENSHOT)); + thumbnail3Dock->setWidget(thumbnail3Widget); + + mainwindow->addDockWidget(static_cast(thumbnail3Dock->property("default_area").toInt()), thumbnail3Dock); + + mainwindow->tabifyDockWidget(thumbnailDock, thumbnail2Dock); + mainwindow->tabifyDockWidget(thumbnailDock, thumbnail3Dock); + + /* when tabifying the dock widgets, the last tab added is selected by default, so we need to re-select the first tab */ + thumbnailDock->raise(); + + coreSelectionWidget = new QWidget(); + coreSelectionWidget->setLayout(new QVBoxLayout()); + + launchWithComboBox = mainwindow->launchWithComboBox(); + + launchWithWidgetLayout = new QVBoxLayout(); + + launchWithWidget = new QWidget(); + launchWithWidget->setLayout(launchWithWidgetLayout); + + coreComboBoxLayout = new QHBoxLayout(); + + mainwindow->runPushButton()->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding)); + mainwindow->stopPushButton()->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding)); + mainwindow->startCorePushButton()->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding)); + + coreComboBoxLayout->addWidget(launchWithComboBox); + coreComboBoxLayout->addWidget(mainwindow->startCorePushButton()); + coreComboBoxLayout->addWidget(mainwindow->coreInfoPushButton()); + coreComboBoxLayout->addWidget(mainwindow->runPushButton()); + coreComboBoxLayout->addWidget(mainwindow->stopPushButton()); + + mainwindow->stopPushButton()->hide(); + + coreComboBoxLayout->setStretchFactor(launchWithComboBox, 1); + + launchWithWidgetLayout->addLayout(coreComboBoxLayout); + + coreSelectionWidget->layout()->addWidget(launchWithWidget); + + coreSelectionWidget->layout()->addItem(new QSpacerItem(20, browserAndPlaylistTabWidget->height(), QSizePolicy::Minimum, QSizePolicy::Expanding)); + + coreSelectionDock = new QDockWidget(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CORE), mainwindow); + coreSelectionDock->setObjectName("coreSelectionDock"); + coreSelectionDock->setProperty("default_area", Qt::LeftDockWidgetArea); + coreSelectionDock->setProperty("menu_text", msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CORE)); + coreSelectionDock->setWidget(coreSelectionWidget); + + mainwindow->addDockWidget(static_cast(coreSelectionDock->property("default_area").toInt()), coreSelectionDock); + + mainwindow->splitDockWidget(browserAndPlaylistTabDock, coreSelectionDock, Qt::Vertical); + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) + mainwindow->resizeDocks(QList() << coreSelectionDock, QList() << 1, Qt::Vertical); #endif + /* this should come last */ + mainwindow->resizeThumbnails(true, true, true); + + if (qsettings->contains("geometry")) + if (qsettings->contains("save_geometry")) + mainwindow->restoreGeometry(qsettings->value("geometry").toByteArray()); + + if (qsettings->value("save_dock_positions", false).toBool()) + if (qsettings->contains("dock_positions")) + mainwindow->restoreState(qsettings->value("dock_positions").toByteArray()); + + if (qsettings->value("save_last_tab", false).toBool()) + { + if (qsettings->contains("last_tab")) + { + int lastTabIndex = qsettings->value("last_tab", 0).toInt(); + + if (lastTabIndex >= 0 && browserAndPlaylistTabWidget->count() > lastTabIndex) + browserAndPlaylistTabWidget->setCurrentIndex(lastTabIndex); + } + } + + if (qsettings->contains("theme")) + { + QString themeStr = qsettings->value("theme").toString(); + MainWindow::Theme theme = mainwindow->getThemeFromString(themeStr); + + if (qsettings->contains("custom_theme") && theme == MainWindow::THEME_CUSTOM) + { + QString customThemeFilePath = qsettings->value("custom_theme").toString(); + + mainwindow->setCustomThemeFile(customThemeFilePath); + } + + mainwindow->setTheme(theme); + } + else + mainwindow->setTheme(); + return handle; } @@ -115,23 +498,41 @@ static void ui_companion_qt_notify_content_loaded(void *data) (void)data; } -static void ui_companion_qt_toggle(void *data) +static void ui_companion_qt_toggle(void *data, bool force) { - ui_companion_qt_init(); + ui_companion_qt_t *handle = (ui_companion_qt_t*)data; + ui_window_qt_t *win_handle = (ui_window_qt_t*)handle->window; + settings_t *settings = config_get_ptr(); + +#ifdef HAVE_DYNAMIC + if (!retroarch_core_set_on_cmdline()) +#endif + if (settings->bools.ui_companion_toggle || force) + { + video_driver_show_mouse(); + win_handle->qtWindow->show(); + + if (video_driver_started_fullscreen()) + win_handle->qtWindow->lower(); + + if (!already_started) + { + already_started = true; + + if (win_handle->qtWindow->settings()->value("show_welcome_screen", true).toBool()) + win_handle->qtWindow->showWelcomeScreen(); + } + } } static void ui_companion_qt_event_command(void *data, enum event_command cmd) { ui_companion_qt_t *handle = (ui_companion_qt_t*)data; + (void)cmd; + if (!handle) return; - -#ifdef HAVE_QT_WRAPPER - slock_lock(handle->lock); - command_event(cmd, NULL); - slock_unlock(handle->lock); -#endif } static void ui_companion_qt_notify_list_pushed(void *data, file_list_t *list, @@ -142,7 +543,37 @@ static void ui_companion_qt_notify_list_pushed(void *data, file_list_t *list, (void)menu_list; } -const ui_companion_driver_t ui_companion_qt = { +static void ui_companion_qt_notify_refresh(void *data) +{ + ui_companion_qt_t *handle = (ui_companion_qt_t*)data; + ui_window_qt_t *win_handle = (ui_window_qt_t*)handle->window; + + win_handle->qtWindow->deferReloadPlaylists(); +} + +static void ui_companion_qt_log_msg(void *data, const char *msg) +{ + ui_companion_qt_t *handle = (ui_companion_qt_t*)data; + ui_window_qt_t *win_handle = (ui_window_qt_t*)handle->window; + + win_handle->qtWindow->appendLogMessage(msg); +} + +void ui_companion_qt_msg_queue_push(void *data, const char *msg, unsigned priority, unsigned duration, bool flush) +{ + ui_companion_qt_t *handle = (ui_companion_qt_t*)data; + ui_window_qt_t *win_handle = NULL; + + if (!handle) + return; + + win_handle = (ui_window_qt_t*)handle->window; + + if (win_handle) + win_handle->qtWindow->showStatusMessage(msg, priority, duration, flush); +} + +ui_companion_driver_t ui_companion_qt = { ui_companion_qt_init, ui_companion_qt_deinit, ui_companion_qt_iterate, @@ -150,20 +581,14 @@ const ui_companion_driver_t ui_companion_qt = { ui_companion_qt_event_command, ui_companion_qt_notify_content_loaded, ui_companion_qt_notify_list_pushed, + ui_companion_qt_notify_refresh, + ui_companion_qt_msg_queue_push, NULL, NULL, - NULL, - NULL, -#ifdef HAVE_QT_WRAPPER - NULL, - NULL, - NULL, - NULL, -#else + ui_companion_qt_log_msg, &ui_browser_window_qt, &ui_msg_window_qt, &ui_window_qt, &ui_application_qt, -#endif "qt", }; diff --git a/ui/drivers/ui_qt.h b/ui/drivers/ui_qt.h new file mode 100644 index 0000000000..db8fbec0ab --- /dev/null +++ b/ui/drivers/ui_qt.h @@ -0,0 +1,366 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2018 - Brad Parker + * + * RetroArch 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 Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch 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 RetroArch. + * If not, see . + */ + +#ifndef _QT_UI +#define _QT_UI + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern "C" { +#include +#include "../ui_companion_driver.h" +} + +class QApplication; +class QCloseEvent; +class QKeyEvent; +class QTimer; +class QFileSystemModel; +class QListWidget; +class QListWidgetItem; +class QTableWidgetItem; +class QResizeEvent; +class QDockWidget; +class QComboBox; +class QPushButton; +class QToolButton; +class QTabWidget; +class QPixmap; +class QPaintEvent; +class QSettings; +class QCheckBox; +class QFormLayout; +class QStyle; +class QScrollArea; +class LoadCoreWindow; +class MainWindow; + +class ThumbnailWidget : public QWidget +{ + Q_OBJECT +public: + ThumbnailWidget(QWidget *parent = 0); + QSize sizeHint() const; +protected: + void paintEvent(QPaintEvent *event); + void resizeEvent(QResizeEvent *event); +}; + +class ThumbnailLabel : public QWidget +{ + Q_OBJECT +public: + ThumbnailLabel(QWidget *parent = 0); + ~ThumbnailLabel(); + QSize sizeHint() const; +public slots: + void setPixmap(const QPixmap &pixmap); +protected: + void paintEvent(QPaintEvent *event); + void resizeEvent(QResizeEvent *event); +private: + void updateMargins(); + + QPixmap *m_pixmap; + int m_pixmapWidth; + int m_pixmapHeight; +}; + +class TreeView : public QTreeView +{ + Q_OBJECT +public: + TreeView(QWidget *parent = 0); +signals: + void itemsSelected(QModelIndexList selectedIndexes); +protected slots: + void columnCountChanged(int oldCount, int newCount); + void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); +}; + +class TableWidget : public QTableWidget +{ + Q_OBJECT +public: + TableWidget(QWidget *parent = 0); +signals: + void enterPressed(); +protected slots: + void keyPressEvent(QKeyEvent *event); +}; + +class AppHandler : public QObject +{ + Q_OBJECT + +public: + AppHandler(QObject *parent = 0); + ~AppHandler(); + void exit(); + bool isExiting() const; + +private slots: + void onLastWindowClosed(); +}; + +class ViewOptionsDialog : public QDialog +{ + Q_OBJECT +public: + ViewOptionsDialog(MainWindow *mainwindow, QWidget *parent = 0); +public slots: + void showDialog(); + void hideDialog(); + void onAccepted(); + void onRejected(); +private slots: + void onThemeComboBoxIndexChanged(int index); + void onHighlightColorChoose(); +private: + void loadViewOptions(); + void saveViewOptions(); + void showOrHideHighlightColor(); + + MainWindow *m_mainwindow; + QSettings *m_settings; + QCheckBox *m_saveGeometryCheckBox; + QCheckBox *m_saveDockPositionsCheckBox; + QCheckBox *m_saveLastTabCheckBox; + QCheckBox *m_showHiddenFilesCheckBox; + QComboBox *m_themeComboBox; + QPushButton *m_highlightColorPushButton; + QColor m_highlightColor; + QLabel *m_highlightColorLabel; + QString m_customThemePath; +}; + +class CoreInfoLabel : public QLabel +{ + Q_OBJECT +public: + CoreInfoLabel(QString text = QString(), QWidget *parent = 0); +}; + +class CoreInfoDialog : public QDialog +{ + Q_OBJECT +public: + CoreInfoDialog(MainWindow *mainwindow, QWidget *parent = 0); +public slots: + void showCoreInfo(); +private: + QFormLayout *m_formLayout; + MainWindow *m_mainwindow; +}; + +class CoreInfoWidget : public QWidget +{ + Q_OBJECT +public: + CoreInfoWidget(CoreInfoLabel *label, QWidget *parent = 0); + QSize sizeHint() const; +protected: + void resizeEvent(QResizeEvent *event); +private: + CoreInfoLabel *m_label; + QScrollArea *m_scrollArea; +}; + +class LogTextEdit : public QPlainTextEdit +{ + Q_OBJECT +public: + LogTextEdit(QWidget *parent = 0); +public slots: + void appendMessage(const QString& text); +}; + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + enum Theme + { + THEME_SYSTEM_DEFAULT, + THEME_DARK, + THEME_CUSTOM + }; + + enum MessageBoxType + { + MSGBOX_TYPE_INFO, + MSGBOX_TYPE_WARNING, + MSGBOX_TYPE_ERROR, + }; + + MainWindow(QWidget *parent = NULL); + ~MainWindow(); + TreeView* dirTreeView(); + QListWidget* playlistListWidget(); + TableWidget* contentTableWidget(); + QWidget* searchWidget(); + QLineEdit* searchLineEdit(); + QComboBox* launchWithComboBox(); + QToolButton* startCorePushButton(); + QToolButton* coreInfoPushButton(); + QToolButton* runPushButton(); + QToolButton* stopPushButton(); + QTabWidget* browserAndPlaylistTabWidget(); + QList > getPlaylistDefaultCores(); + ViewOptionsDialog* viewOptionsDialog(); + QSettings* settings(); + QList > getCoreInfo(); + void setTheme(Theme theme = THEME_SYSTEM_DEFAULT); + Theme theme(); + Theme getThemeFromString(QString themeString); + QString getThemeString(Theme theme); + QHash getSelectedCore(); + void showStatusMessage(QString msg, unsigned priority, unsigned duration, bool flush); + bool showMessageBox(QString msg, MessageBoxType msgType = MSGBOX_TYPE_INFO, Qt::WindowModality modality = Qt::ApplicationModal); + bool setCustomThemeFile(QString filePath); + void setCustomThemeString(QString qss); + const QString& customThemeString() const; + +signals: + void thumbnailChanged(const QPixmap &pixmap); + void thumbnail2Changed(const QPixmap &pixmap); + void thumbnail3Changed(const QPixmap &pixmap); + void gotLogMessage(const QString &msg); + void gotStatusMessage(QString msg, unsigned priority, unsigned duration, bool flush); + void gotReloadPlaylists(); + +public slots: + void onBrowserDownloadsClicked(); + void onBrowserUpClicked(); + void onBrowserStartClicked(); + void initContentTableWidget(); + void onViewClosedDocksAboutToShow(); + void onShowHiddenDockWidgetAction(); + void setCoreActions(); + void onRunClicked(); + void onStartCoreClicked(); + void onTableWidgetEnterPressed(); + void selectBrowserDir(QString path); + void resizeThumbnails(bool one, bool two, bool three); + void onResizeThumbnailOne(); + void onResizeThumbnailTwo(); + void onResizeThumbnailThree(); + void appendLogMessage(const QString &msg); + void onGotLogMessage(const QString &msg); + void onGotStatusMessage(QString msg, unsigned priority, unsigned duration, bool flush); + void reloadPlaylists(); + void deferReloadPlaylists(); + void onGotReloadPlaylists(); + void showWelcomeScreen(); + +private slots: + void onLoadCoreClicked(const QStringList &extensionFilters = QStringList()); + void onUnloadCoreMenuAction(); + void onTimeout(); + void onCoreLoaded(); + void onCurrentListItemChanged(QListWidgetItem *current, QListWidgetItem *previous); + void onCurrentTableItemChanged(QTableWidgetItem *current, QTableWidgetItem *previous); + void onSearchEnterPressed(); + void onSearchLineEditEdited(const QString &text); + void addPlaylistItemsToTable(QString path); + void onContentItemDoubleClicked(QTableWidgetItem *item); + void onCoreLoadWindowClosed(); + void onTabWidgetIndexChanged(int index); + void onTreeViewItemsSelected(QModelIndexList selectedIndexes); + void onSearchResetClicked(); + void onLaunchWithComboBoxIndexChanged(int index); + void onFileBrowserTreeContextMenuRequested(const QPoint &pos); + void onPlaylistWidgetContextMenuRequested(const QPoint &pos); + void onStopClicked(); + +private: + void setCurrentCoreLabel(); + void getPlaylistFiles(); + bool isCoreLoaded(); + bool isContentLessCore(); + + LoadCoreWindow *m_loadCoreWindow; + QTimer *m_timer; + QString m_currentCore; + QString m_currentCoreVersion; + QLabel *m_statusLabel; + TreeView *m_dirTree; + QFileSystemModel *m_dirModel; + QListWidget *m_listWidget; + TableWidget *m_tableWidget; + QWidget *m_searchWidget; + QLineEdit *m_searchLineEdit; + QDockWidget *m_searchDock; + QStringList m_playlistFiles; + QComboBox *m_launchWithComboBox; + QToolButton *m_startCorePushButton; + QToolButton *m_coreInfoPushButton; + QToolButton *m_runPushButton; + QToolButton *m_stopPushButton; + QTabWidget *m_browserAndPlaylistTabWidget; + bool m_pendingRun; + QPixmap *m_thumbnailPixmap; + QPixmap *m_thumbnailPixmap2; + QPixmap *m_thumbnailPixmap3; + QRegularExpression m_fileSanitizerRegex; + QSettings *m_settings; + ViewOptionsDialog *m_viewOptionsDialog; + CoreInfoDialog *m_coreInfoDialog; + QStyle *m_defaultStyle; + QPalette m_defaultPalette; + Theme m_currentTheme; + QDockWidget *m_coreInfoDock; + CoreInfoLabel *m_coreInfoLabel; + CoreInfoWidget *m_coreInfoWidget; + QDockWidget *m_logDock; + QWidget *m_logWidget; + LogTextEdit *m_logTextEdit; + QVector m_imageFormats; + QListWidgetItem *m_historyPlaylistsItem; + QIcon m_folderIcon; + QString m_customThemeString; + +protected: + void closeEvent(QCloseEvent *event); + void keyPressEvent(QKeyEvent *event); +}; + +RETRO_BEGIN_DECLS + +typedef struct ui_application_qt +{ + QApplication *app; +} ui_application_qt_t; + +typedef struct ui_window_qt +{ + MainWindow *qtWindow; +} ui_window_qt_t; + +RETRO_END_DECLS + +#endif diff --git a/ui/drivers/ui_win32.c b/ui/drivers/ui_win32.c index ec7c2265cc..9b7eff96cf 100644 --- a/ui/drivers/ui_win32.c +++ b/ui/drivers/ui_win32.c @@ -149,7 +149,7 @@ static void shader_dlg_params_refresh(void) { case SHADER_PARAM_CTRL_CHECKBOX: { - bool checked; + bool checked; video_shader_ctx_t shader_info; video_shader_driver_get_current_shader(&shader_info); @@ -769,9 +769,10 @@ static void ui_companion_win32_notify_content_loaded(void *data) (void)data; } -static void ui_companion_win32_toggle(void *data) +static void ui_companion_win32_toggle(void *data, bool force) { (void)data; + (void)force; } static void ui_companion_win32_event_command( @@ -789,7 +790,7 @@ static void ui_companion_win32_notify_list_pushed(void *data, (void)menu_list; } -const ui_companion_driver_t ui_companion_win32 = { +ui_companion_driver_t ui_companion_win32 = { ui_companion_win32_init, ui_companion_win32_deinit, ui_companion_win32_iterate, @@ -801,6 +802,7 @@ const ui_companion_driver_t ui_companion_win32 = { NULL, NULL, NULL, + NULL, &ui_browser_window_win32, &ui_msg_window_win32, &ui_window_win32, diff --git a/ui/drivers/win32/ui_win32_application.c b/ui/drivers/win32/ui_win32_application.c index be5c8eace7..b40f927fcd 100644 --- a/ui/drivers/win32/ui_win32_application.c +++ b/ui/drivers/win32/ui_win32_application.c @@ -23,9 +23,9 @@ #include "../../ui_companion_driver.h" -static bool ui_application_win32_initialize(void) +static void* ui_application_win32_initialize(void) { - return true; + return NULL; } static bool ui_application_win32_pending_events(void) @@ -48,9 +48,16 @@ static void ui_application_win32_process_events(void) } } -const ui_application_t ui_application_win32 = { +static void ui_application_win32_run(void *args) +{ + (void)args; +} + +ui_application_t ui_application_win32 = { ui_application_win32_initialize, ui_application_win32_pending_events, ui_application_win32_process_events, + ui_application_win32_run, + NULL, "win32" }; diff --git a/ui/drivers/win32/ui_win32_browser_window.c b/ui/drivers/win32/ui_win32_browser_window.c index 6ebcc3b670..bf653fd77e 100644 --- a/ui/drivers/win32/ui_win32_browser_window.c +++ b/ui/drivers/win32/ui_win32_browser_window.c @@ -74,7 +74,7 @@ static bool ui_browser_window_win32_save(ui_browser_window_state_t *state) return ui_browser_window_win32_core(state, true); } -const ui_browser_window_t ui_browser_window_win32 = { +ui_browser_window_t ui_browser_window_win32 = { ui_browser_window_win32_open, ui_browser_window_win32_save, "win32" diff --git a/ui/drivers/win32/ui_win32_msg_window.c b/ui/drivers/win32/ui_win32_msg_window.c index 113a3d58eb..8ef8a17079 100644 --- a/ui/drivers/win32/ui_win32_msg_window.c +++ b/ui/drivers/win32/ui_win32_msg_window.c @@ -97,7 +97,7 @@ static enum ui_msg_window_response ui_msg_window_win32_warning(ui_msg_window_sta return ui_msg_window_win32_response(state, MessageBoxA(NULL, (LPCSTR)state->text, (LPCSTR)state->title, flags)); } -const ui_msg_window_t ui_msg_window_win32 = { +ui_msg_window_t ui_msg_window_win32 = { ui_msg_window_win32_error, ui_msg_window_win32_information, ui_msg_window_win32_question, diff --git a/ui/drivers/win32/ui_win32_window.c b/ui/drivers/win32/ui_win32_window.c index bd39aa50d6..089b12b877 100644 --- a/ui/drivers/win32/ui_win32_window.c +++ b/ui/drivers/win32/ui_win32_window.c @@ -50,6 +50,11 @@ #include "../../../retroarch.h" #include "../../../tasks/tasks_internal.h" +static void* ui_window_win32_init(void) +{ + return NULL; +} + static void ui_window_win32_destroy(void *data) { ui_window_win32_t *window = (ui_window_win32_t*)data; @@ -89,7 +94,8 @@ static bool ui_window_win32_focused(void *data) return (GetForegroundWindow() == window->hwnd); } -const ui_window_t ui_window_win32 = { +ui_window_t ui_window_win32 = { + ui_window_win32_init, ui_window_win32_destroy, ui_window_win32_set_focused, ui_window_win32_set_visible, diff --git a/ui/ui_companion_driver.c b/ui/ui_companion_driver.c index d75df1f80d..ffe2592e9a 100644 --- a/ui/ui_companion_driver.c +++ b/ui/ui_companion_driver.c @@ -26,11 +26,7 @@ #include "ui_companion_driver.h" - static const ui_companion_driver_t *ui_companion_drivers[] = { -#ifdef HAVE_QT_WRAPPER - &ui_companion_qt, -#endif #if defined(_WIN32) && !defined(_XBOX) &ui_companion_win32, #endif @@ -39,17 +35,18 @@ static const ui_companion_driver_t *ui_companion_drivers[] = { #endif #ifdef HAVE_COCOATOUCH &ui_companion_cocoatouch, -#endif -#ifdef HAVE_QT - &ui_companion_qt, #endif &ui_companion_null, NULL }; -static bool main_ui_companion_is_on_foreground; -static const ui_companion_driver_t *ui_companion; -static void *ui_companion_data; +static bool main_ui_companion_is_on_foreground = false; +static const ui_companion_driver_t *ui_companion = NULL; +static void *ui_companion_data = NULL; + +#ifdef HAVE_QT +static void *ui_companion_qt_data = NULL; +#endif /** * ui_companion_find_driver: @@ -114,6 +111,11 @@ void ui_companion_driver_deinit(void) return; if (ui->deinit) ui->deinit(ui_companion_data); + +#ifdef HAVE_QT + ui_companion_qt.deinit(ui_companion_qt_data); + ui_companion_qt_data = NULL; +#endif ui_companion_data = NULL; } @@ -123,17 +125,36 @@ void ui_companion_driver_init_first(void) ui_companion = (ui_companion_driver_t*)ui_companion_init_first(); - if (ui_companion && ui_companion->toggle) +#ifdef HAVE_QT + ui_companion_qt_data = ui_companion_qt.init(); +#endif + + if (ui_companion) { if (settings->bools.ui_companion_start_on_boot) - ui_companion->toggle(ui_companion_data); + { + if (ui_companion->init) + ui_companion_data = ui_companion->init(); + + if (ui_companion->toggle) + ui_companion->toggle(ui_companion_data, false); + +#ifdef HAVE_QT + ui_companion_qt.toggle(ui_companion_qt_data, false); +#endif + } } } -void ui_companion_driver_toggle(void) +void ui_companion_driver_toggle(bool force) { if (ui_companion && ui_companion->toggle) - ui_companion->toggle(ui_companion_data); + ui_companion->toggle(ui_companion_data, false); + +#ifdef HAVE_QT + if (ui_companion_qt.toggle) + ui_companion_qt.toggle(ui_companion_qt_data, force); +#endif } void ui_companion_driver_notify_refresh(void) @@ -143,6 +164,10 @@ void ui_companion_driver_notify_refresh(void) return; if (ui->notify_refresh) ui->notify_refresh(ui_companion_data); +#ifdef HAVE_QT + if (ui_companion_qt.notify_refresh) + ui_companion_qt.notify_refresh(ui_companion_qt_data); +#endif } void ui_companion_driver_notify_list_loaded(file_list_t *list, file_list_t *menu_list) @@ -192,6 +217,13 @@ const ui_browser_window_t *ui_companion_driver_get_browser_window_ptr(void) return ui->browser_window; } +#ifdef HAVE_QT +const ui_application_t *ui_companion_driver_get_qt_application_ptr(void) +{ + return ui_companion_qt.application; +} +#endif + const ui_application_t *ui_companion_driver_get_application_ptr(void) { const ui_companion_driver_t *ui = ui_companion_get_ptr(); @@ -200,6 +232,17 @@ const ui_application_t *ui_companion_driver_get_application_ptr(void) return ui->application; } +void ui_companion_driver_msg_queue_push(const char *msg, unsigned priority, unsigned duration, bool flush) +{ + const ui_companion_driver_t *ui = ui_companion_get_ptr(); + if (ui && ui->msg_queue_push) + ui->msg_queue_push(ui_companion_data, msg, priority, duration, flush); +#ifdef HAVE_QT + if (ui_companion_qt.msg_queue_push) + ui_companion_qt.msg_queue_push(ui_companion_qt_data, msg, priority, duration, flush); +#endif +} + void *ui_companion_driver_get_main_window(void) { const ui_companion_driver_t *ui = ui_companion_get_ptr(); @@ -215,3 +258,13 @@ const char *ui_companion_driver_get_ident(void) return "null"; return ui->ident; } + +void ui_companion_driver_log_msg(const char *msg) +{ + (void)msg; + +#ifdef HAVE_QT + if (ui_companion_qt_data) + ui_companion_qt.log_msg(ui_companion_qt_data, msg); +#endif +} diff --git a/ui/ui_companion_driver.h b/ui/ui_companion_driver.h index ab93c114e2..f358843272 100644 --- a/ui/ui_companion_driver.h +++ b/ui/ui_companion_driver.h @@ -104,14 +104,17 @@ typedef struct ui_msg_window typedef struct ui_application { - bool (*initialize)(void); + void* (*initialize)(void); bool (*pending_events)(void); void (*process_events)(void); + void (*run)(void *args); + void (*quit)(void); const char *ident; } ui_application_t; typedef struct ui_window { + void* (*init)(void); void (*destroy)(void *data); void (*set_focused)(void *data); void (*set_visible)(void *data, bool visible); @@ -126,46 +129,47 @@ typedef struct ui_companion_driver void *(*init)(void); void (*deinit)(void *data); int (*iterate)(void *data, unsigned action); - void (*toggle)(void *data); + void (*toggle)(void *data, bool force); void (*event_command)(void *data, enum event_command action); void (*notify_content_loaded)(void *data); void (*notify_list_loaded)(void *data, file_list_t *list, file_list_t *menu_list); void (*notify_refresh)(void *data); - void (*msg_queue_push)(const char *msg, unsigned priority, unsigned duration, bool flush); + void (*msg_queue_push)(void *data, const char *msg, unsigned priority, unsigned duration, bool flush); void (*render_messagebox)(const char *msg); void *(*get_main_window)(void *data); - const ui_browser_window_t *browser_window; - const ui_msg_window_t *msg_window; - const ui_window_t *window; - const ui_application_t *application; + void (*log_msg)(void *data, const char *msg); + ui_browser_window_t *browser_window; + ui_msg_window_t *msg_window; + ui_window_t *window; + ui_application_t *application; const char *ident; } ui_companion_driver_t; -extern const ui_browser_window_t ui_browser_window_null; -extern const ui_browser_window_t ui_browser_window_cocoa; -extern const ui_browser_window_t ui_browser_window_qt; -extern const ui_browser_window_t ui_browser_window_win32; +extern ui_browser_window_t ui_browser_window_null; +extern ui_browser_window_t ui_browser_window_cocoa; +extern ui_browser_window_t ui_browser_window_qt; +extern ui_browser_window_t ui_browser_window_win32; -extern const ui_window_t ui_window_null; -extern const ui_window_t ui_window_cocoa; -extern const ui_window_t ui_window_qt; -extern const ui_window_t ui_window_win32; +extern ui_window_t ui_window_null; +extern ui_window_t ui_window_cocoa; +extern ui_window_t ui_window_qt; +extern ui_window_t ui_window_win32; -extern const ui_msg_window_t ui_msg_window_null; -extern const ui_msg_window_t ui_msg_window_win32; -extern const ui_msg_window_t ui_msg_window_qt; -extern const ui_msg_window_t ui_msg_window_cocoa; +extern ui_msg_window_t ui_msg_window_null; +extern ui_msg_window_t ui_msg_window_win32; +extern ui_msg_window_t ui_msg_window_qt; +extern ui_msg_window_t ui_msg_window_cocoa; -extern const ui_application_t ui_application_null; -extern const ui_application_t ui_application_cocoa; -extern const ui_application_t ui_application_qt; -extern const ui_application_t ui_application_win32; +extern ui_application_t ui_application_null; +extern ui_application_t ui_application_cocoa; +extern ui_application_t ui_application_qt; +extern ui_application_t ui_application_win32; -extern const ui_companion_driver_t ui_companion_null; -extern const ui_companion_driver_t ui_companion_cocoa; -extern const ui_companion_driver_t ui_companion_cocoatouch; -extern const ui_companion_driver_t ui_companion_qt; -extern const ui_companion_driver_t ui_companion_win32; +extern ui_companion_driver_t ui_companion_null; +extern ui_companion_driver_t ui_companion_cocoa; +extern ui_companion_driver_t ui_companion_cocoatouch; +extern ui_companion_driver_t ui_companion_qt; +extern ui_companion_driver_t ui_companion_win32; /** * ui_companion_find_driver: @@ -204,7 +208,7 @@ void ui_companion_driver_notify_list_loaded(file_list_t *list, file_list_t *menu void ui_companion_driver_notify_content_loaded(void); -void ui_companion_driver_toggle(void); +void ui_companion_driver_toggle(bool force); void ui_companion_driver_free(void); @@ -216,6 +220,14 @@ const ui_window_t *ui_companion_driver_get_window_ptr(void); const ui_application_t *ui_companion_driver_get_application_ptr(void); +#ifdef HAVE_QT +const ui_application_t *ui_companion_driver_get_qt_application_ptr(void); +#endif + +void ui_companion_driver_log_msg(const char *msg); + +void ui_companion_driver_msg_queue_push(const char *msg, unsigned priority, unsigned duration, bool flush); + void *ui_companion_driver_get_main_window(void); const char *ui_companion_driver_get_ident(void); diff --git a/verbosity.c b/verbosity.c index 6102669a78..85a207471e 100644 --- a/verbosity.c +++ b/verbosity.c @@ -52,6 +52,10 @@ #include "file_path_special.h" #include "verbosity.h" +#ifdef HAVE_QT +#include "ui/ui_companion_driver.h" +#endif + /* If this is non-NULL. RARCH_LOG and friends * will write to this file. */ static FILE *log_file_fp = NULL; @@ -174,16 +178,35 @@ void RARCH_LOG_V(const char *tag, const char *fmt, va_list ap) #else { +#ifdef HAVE_QT + char buffer[1024]; +#endif #ifdef HAVE_FILE_LOGGER FILE *fp = (FILE*)retro_main_log_file(); #else FILE *fp = stderr; #endif - fprintf(fp, "%s ", - tag ? tag : file_path_str(FILE_PATH_LOG_INFO)); - vfprintf(fp, fmt, ap); - fflush(fp); +#ifdef HAVE_QT + buffer[0] = '\0'; + vsnprintf(buffer, sizeof(buffer), fmt, ap); + + if (fp) + { + fprintf(fp, "%s", buffer); + fflush(fp); + } + + ui_companion_driver_log_msg(buffer); +#else + if (fp) + { + fprintf(fp, "%s ", + tag ? tag : file_path_str(FILE_PATH_LOG_INFO)); + vfprintf(fp, fmt, ap); + fflush(fp); + } +#endif } #endif }