diff --git a/cmake/Findgdk.cmake b/cmake/Findgdk.cmake new file mode 100644 index 0000000..b3975c4 --- /dev/null +++ b/cmake/Findgdk.cmake @@ -0,0 +1,37 @@ +find_package(PkgConfig) +if (PKG_CONFIG_FOUND) + pkg_check_modules(PC_gdk QUIET gdk-3.0) + set(gdk_DEFINITIONS ${PC_gdk_CFLAGS_OTHER}) +endif () + +set(gdk_INCLUDE_HINTS ${PC_gdk_INCLUDEDIR} ${PC_gdk_INCLUDE_DIRS}) +set(gdk_LIBRARY_HINTS ${PC_gdk_LIBDIR} ${PC_gdk_LIBRARY_DIRS}) + +find_path(gdk_INCLUDE_DIR + NAMES gdk/gdk.h + HINTS ${gdk_INCLUDE_HINTS} + /usr/include + /usr/local/include + /opt/local/include + PATH_SUFFIXES gdk-3.0) + +find_library(gdk_LIBRARY + NAMES gdk-3.0 + gdk-3 + gdk + HINTS ${gdk_LIBRARY_HINTS} + /usr/lib + /usr/local/lib + /opt/local/lib) + +set(gdk_LIBRARIES ${gdk_LIBRARY}) +set(gdk_INCLUDE_DIRS ${gdk_INCLUDE_DIR}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(gdk + REQUIRED_VARS + gdk_LIBRARY + gdk_INCLUDE_DIR + VERSION_VAR gdk_VERSION) + +mark_as_advanced(gdk_INCLUDE_DIR gdk_LIBRARY) diff --git a/cmake/Findgdkmm.cmake b/cmake/Findgdkmm.cmake index b619270..5316bb7 100644 --- a/cmake/Findgdkmm.cmake +++ b/cmake/Findgdkmm.cmake @@ -1,48 +1,50 @@ -set(GDKMM_LIBRARY_NAME gdkmm-3.0) +set(gdkmm_LIBRARY_NAME gdkmm-3.0) find_package(PkgConfig) if (PKG_CONFIG_FOUND) - pkg_check_modules(PKGCONFIG_GDKMM QUIET ${GDKMM_LIBRARY_NAME}) - set(GDKMM_DEFINITIONS ${PKGCONFIG_GDKMM_CFLAGS_OTHER}) + pkg_check_modules(PKGCONFIG_gdkmm QUIET ${gdkmm_LIBRARY_NAME}) + set(gdkmm_DEFINITIONS ${PKGCONFIG_gdkmm_CFLAGS_OTHER}) endif (PKG_CONFIG_FOUND) -set(GDKMM_INCLUDE_HINTS ${PKGCONFIG_GDKMM_INCLUDEDIR} ${PKGCONFIG_GDKMM_INCLUDE_DIRS}) -set(GDKMM_LIBRARY_HINTS ${PKGCONFIG_GDKMM_LIBDIR} ${PKGCONFIG_GDKMM_LIBRARY_DIRS}) +set(gdkmm_INCLUDE_HINTS ${PKGCONFIG_gdkmm_INCLUDEDIR} ${PKGCONFIG_gdkmm_INCLUDE_DIRS}) +set(gdkmm_LIBRARY_HINTS ${PKGCONFIG_gdkmm_LIBDIR} ${PKGCONFIG_gdkmm_LIBRARY_DIRS}) -find_path(GDKMM_INCLUDE_DIR +find_path(gdkmm_INCLUDE_DIR NAMES gdkmm.h - HINTS ${GDKMM_INCLUDE_HINTS} + HINTS ${gdkmm_INCLUDE_HINTS} /usr/include /usr/local/include /opt/local/include - PATH_SUFFIXES ${GDKMM_LIBRARY_NAME}) + PATH_SUFFIXES ${gdkmm_LIBRARY_NAME}) -find_path(GDKMM_CONFIG_INCLUDE_DIR +find_path(gdkmm_CONFIG_INCLUDE_DIR NAMES gdkmmconfig.h - HINTS ${GDKMM_LIBRARY_HINTS} + HINTS ${gdkmm_LIBRARY_HINTS} /usr/lib /usr/local/lib /opt/local/lib - PATH_SUFFIXES ${GDKMM_LIBRARY_NAME}/include) + PATH_SUFFIXES ${gdkmm_LIBRARY_NAME}/include) -find_library(GDKMM_LIBRARY - NAMES ${GDKMM_LIBRARY_NAME} - gdkmm - HINTS ${GDKMM_LIBRARY_HINTS} +find_library(gdkmm_LIBRARY + NAMES ${gdkmm_LIBRARY_NAME} + gdkmm + HINTS ${gdkmm_LIBRARY_HINTS} /usr/lib /usr/local/lib /opt/local/lib - PATH_SUFFIXES ${GDKMM_LIBRARY_NAME} - ${GDKMM_LIBRARY_NAME}/include) + PATH_SUFFIXES ${gdkmm_LIBRARY_NAME} + ${gdkmm_LIBRARY_NAME}/include) -set(GDKMM_LIBRARIES ${GDKMM_LIBRARY}) -set(GDKMM_INCLUDE_DIRS ${GDKMM_INCLUDE_DIR};${GDKMM_CONFIG_INCLUDE_DIRS};${GDKMM_CONFIG_INCLUDE_DIR}) +find_package(gdk) + +set(gdkmm_LIBRARIES ${gdkmm_LIBRARY};${gdk_LIBRARIES}) +set(gdkmm_INCLUDE_DIRS ${gdkmm_INCLUDE_DIR};${gdkmm_CONFIG_INCLUDE_DIRS};${gdkmm_CONFIG_INCLUDE_DIR};${gdk_INCLUDE_DIRS}) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(gdkmm REQUIRED_VARS - GDKMM_LIBRARY - GDKMM_INCLUDE_DIRS - VERSION_VAR GDKMM_VERSION) + gdkmm_LIBRARY + gdkmm_INCLUDE_DIRS + VERSION_VAR gdkmm_VERSION) -mark_as_advanced(GDKMM_INCLUDE_DIR GDKMM_LIBRARY) +mark_as_advanced(gdkmm_INCLUDE_DIR gdkmm_LIBRARY) diff --git a/cmake/Findgtkmm.cmake b/cmake/Findgtkmm.cmake index 479c1f3..addbede 100644 --- a/cmake/Findgtkmm.cmake +++ b/cmake/Findgtkmm.cmake @@ -1,13 +1,13 @@ -set(GTKMM_LIBRARY_NAME gtkmm-3.0) -set(GDKMM_LIBRARY_NAME gdkmm-3.0) +set(GTKMM_LIBRARY_NAME gtkmm-3.0) +set(GDKMM_LIBRARY_NAME gdkmm-3.0) find_package(PkgConfig) -if(PKG_CONFIG_FOUND) +if (PKG_CONFIG_FOUND) pkg_check_modules(PC_GTKMM QUIET ${GTKMM_LIBRARY_NAME}) pkg_check_modules(PC_GDKMM QUIET ${GDKMM_LIBRARY_NAME}) pkg_check_modules(PC_PANGOMM QUIET ${PANGOMM_LIBRARY_NAME}) - set(GTKMM_DEFINITIONS ${PC_GTKMM_CFLAGS_OTHER}) -endif() + set(GTKMM_DEFINITIONS ${PC_GTKMM_CFLAGS_OTHER}) +endif () find_package(gtk) find_package(glibmm) @@ -46,14 +46,14 @@ find_path(GDKMM_CONFIG_INCLUDE_DIR HINTS ${GDKMM_INCLUDE_HINTS} PATH_SUFFIXES ${GDKMM_LIBRARY_NAME}/include) -set(GTKMM_LIBRARIES ${GTKMM_LIB};${GDKMM_LIBRARY};${GTK_LIBRARIES};${GLIBMM_LIBRARIES};${PANGOMM_LIBRARIES};${CAIROMM_LIBRARIES};${ATKMM_LIBRARIES};${SIGC++_LIBRARIES}) -set(GTKMM_INCLUDE_DIRS ${GTKMM_INCLUDE_DIR};${GTKMM_CONFIG_INCLUDE_DIR};${GDKMM_INCLUDE_DIR};${GDKMM_CONFIG_INCLUDE_DIR};${GTK_INCLUDE_DIRS};${GLIBMM_INCLUDE_DIRS};${PANGOMM_INCLUDE_DIRS};${CAIROMM_INCLUDE_DIRS};${ATKMM_INCLUDE_DIRS};${SIGC++_INCLUDE_DIRS}) +set(GTKMM_LIBRARIES ${GTKMM_LIB};${gdkmm_LIBRARIES};${GTK_LIBRARIES};${GLIBMM_LIBRARIES};${PANGOMM_LIBRARIES};${CAIROMM_LIBRARIES};${ATKMM_LIBRARIES};${SIGC++_LIBRARIES}) +set(GTKMM_INCLUDE_DIRS ${GTKMM_INCLUDE_DIR};${GTKMM_CONFIG_INCLUDE_DIR};${gdkmm_INCLUDE_DIRS};${gdkmm_CONFIG_INCLUDE_DIR};${GTK_INCLUDE_DIRS};${GLIBMM_INCLUDE_DIRS};${PANGOMM_INCLUDE_DIRS};${CAIROMM_INCLUDE_DIRS};${ATKMM_INCLUDE_DIRS};${SIGC++_INCLUDE_DIRS}) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(gtkmm REQUIRED_VARS - GTKMM_LIB - GTKMM_INCLUDE_DIRS + GTKMM_LIB + GTKMM_INCLUDE_DIRS VERSION_VAR GTKMM_VERSION) mark_as_advanced(GTKMM_INCLUDE_DIR GTKMM_LIBRARY) diff --git a/cmake/Findlibhandy.cmake b/cmake/Findlibhandy.cmake index eb6e477..73792f0 100644 --- a/cmake/Findlibhandy.cmake +++ b/cmake/Findlibhandy.cmake @@ -18,7 +18,7 @@ find_path(libhandy_INCLUDE_DIR PATH_SUFFIXES ${libhandy_LIBRARY_NAME}) find_library(libhandy_LIBRARY - NAMES ${libhandy_LIBRARY_NAME} + NAMES ${libhandy_LIBRARY_NAME} handy-1 HINTS ${libhandy_LIBRARY_HINTS} /usr/lib /usr/local/lib diff --git a/src/abaddon.cpp b/src/abaddon.cpp index f7000c1..4ca1462 100644 --- a/src/abaddon.cpp +++ b/src/abaddon.cpp @@ -63,6 +63,39 @@ Abaddon &Abaddon::Get() { return instance; } +#ifdef WITH_LIBHANDY + #ifdef _WIN32 +constexpr static guint BUTTON_BACK = 4; +constexpr static guint BUTTON_FORWARD = 5; + #else +constexpr static guint BUTTON_BACK = 8; +constexpr static guint BUTTON_FORWARD = 9; + #endif + +static void HandleButtonEvents(GdkEvent *event, MainWindow *main_window) { + if (event->type != GDK_BUTTON_PRESS) return; + + auto *widget = gtk_get_event_widget(event); + if (widget == nullptr) return; + auto *window = gtk_widget_get_toplevel(widget); + if (static_cast(window) != static_cast(main_window->gobj())) return; // is this the right way??? + + switch (event->button.button) { + case BUTTON_BACK: + main_window->GoBack(); + break; + case BUTTON_FORWARD: + main_window->GoForward(); + break; + } +} + +static void MainEventHandler(GdkEvent *event, void *main_window) { + HandleButtonEvents(event, static_cast(main_window)); + gtk_main_do_event(event); +} +#endif + int Abaddon::StartGTK() { m_gtk_app = Gtk::Application::create("com.github.uowuo.abaddon"); @@ -113,6 +146,10 @@ int Abaddon::StartGTK() { m_main_window->set_title(APP_TITLE); m_main_window->set_position(Gtk::WIN_POS_CENTER); +#ifdef WITH_LIBHANDY + gdk_event_handler_set(&MainEventHandler, m_main_window.get(), nullptr); +#endif + if (!m_settings.IsValid()) { Gtk::MessageDialog dlg(*m_main_window, "The settings file could not be opened!", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); dlg.set_position(Gtk::WIN_POS_CENTER); diff --git a/src/components/channeltabswitcherhandy.cpp b/src/components/channeltabswitcherhandy.cpp index bb28272..9abceed 100644 --- a/src/components/channeltabswitcherhandy.cpp +++ b/src/components/channeltabswitcherhandy.cpp @@ -57,6 +57,7 @@ void ChannelTabSwitcherHandy::AddChannelTab(Snowflake id) { CheckUnread(id); CheckPageIcon(page, *channel); + AppendPageHistory(page, id); } void ChannelTabSwitcherHandy::ReplaceActiveTab(Snowflake id) { @@ -78,6 +79,7 @@ void ChannelTabSwitcherHandy::ReplaceActiveTab(Snowflake id) { CheckUnread(id); CheckPageIcon(page, *channel); + AppendPageHistory(page, id); } } @@ -103,6 +105,14 @@ void ChannelTabSwitcherHandy::UseTabsState(const TabsState &state) { } } +void ChannelTabSwitcherHandy::GoBackOnCurrent() { + AdvanceOnCurrent(-1); +} + +void ChannelTabSwitcherHandy::GoForwardOnCurrent() { + AdvanceOnCurrent(1); +} + void ChannelTabSwitcherHandy::CheckUnread(Snowflake id) { if (auto it = m_pages.find(id); it != m_pages.end()) { hdy_tab_page_set_needs_attention(it->second, Abaddon::Get().GetDiscordClient().GetUnreadStateForChannel(id) > -1); @@ -143,6 +153,41 @@ void ChannelTabSwitcherHandy::CheckPageIcon(HdyTabPage *page, const ChannelData hdy_tab_page_set_icon(page, nullptr); } +void ChannelTabSwitcherHandy::AppendPageHistory(HdyTabPage *page, Snowflake channel) { + auto it = m_page_history.find(page); + if (it == m_page_history.end()) { + m_page_history[page] = PageHistory { { channel }, 0 }; + return; + } + + // drop everything beyond current position + it->second.Visited.resize(++it->second.CurrentVisitedIndex); + it->second.Visited.push_back(channel); +} + +void ChannelTabSwitcherHandy::AdvanceOnCurrent(size_t by) { + auto *current = hdy_tab_view_get_selected_page(m_tab_view); + if (current == nullptr) return; + auto history = m_page_history.find(current); + if (history == m_page_history.end()) return; + if (by + history->second.CurrentVisitedIndex < 0 || by + history->second.CurrentVisitedIndex >= history->second.Visited.size()) return; + + history->second.CurrentVisitedIndex += by; + const auto to_id = history->second.Visited.at(history->second.CurrentVisitedIndex); + + // temporarily point current index to the end so that it doesnt fuck up the history + // remove it immediately after cuz the emit will call ReplaceActiveTab + const auto real = history->second.CurrentVisitedIndex; + history->second.CurrentVisitedIndex = history->second.Visited.size() - 1; + m_signal_channel_switched_to.emit(to_id); + // iterator might not be valid + history = m_page_history.find(current); + if (history != m_page_history.end()) { + history->second.Visited.pop_back(); + } + history->second.CurrentVisitedIndex = real; +} + ChannelTabSwitcherHandy::type_signal_channel_switched_to ChannelTabSwitcherHandy::signal_channel_switched_to() { return m_signal_channel_switched_to; } diff --git a/src/components/channeltabswitcherhandy.hpp b/src/components/channeltabswitcherhandy.hpp index 41c9c92..37a71ee 100644 --- a/src/components/channeltabswitcherhandy.hpp +++ b/src/components/channeltabswitcherhandy.hpp @@ -22,11 +22,16 @@ public: TabsState GetTabsState(); void UseTabsState(const TabsState &state); + void GoBackOnCurrent(); + void GoForwardOnCurrent(); + private: void CheckUnread(Snowflake id); void ClearPage(HdyTabPage *page); void OnPageIconLoad(HdyTabPage *page, const Glib::RefPtr &pb); void CheckPageIcon(HdyTabPage *page, const ChannelData &data); + void AppendPageHistory(HdyTabPage *page, Snowflake channel); + void AdvanceOnCurrent(size_t by); HdyTabBar *m_tab_bar; Gtk::Widget *m_tab_bar_wrapped; @@ -38,6 +43,13 @@ private: // need to hold a reference to the pixbuf data std::unordered_map> m_page_icons; + struct PageHistory { + std::vector Visited; + size_t CurrentVisitedIndex; + }; + + std::unordered_map m_page_history; + friend void selected_page_notify_cb(HdyTabView *, GParamSpec *, ChannelTabSwitcherHandy *); friend gboolean close_page_cb(HdyTabView *, HdyTabPage *, ChannelTabSwitcherHandy *); diff --git a/src/components/chatwindow.cpp b/src/components/chatwindow.cpp index 2a6a67e..fcaf6c4 100644 --- a/src/components/chatwindow.cpp +++ b/src/components/chatwindow.cpp @@ -180,6 +180,14 @@ TabsState ChatWindow::GetTabsState() { void ChatWindow::UseTabsState(const TabsState &state) { m_tab_switcher->UseTabsState(state); } + +void ChatWindow::GoBack() { + m_tab_switcher->GoBackOnCurrent(); +} + +void ChatWindow::GoForward() { + m_tab_switcher->GoForwardOnCurrent(); +} #endif Snowflake ChatWindow::GetActiveChannel() const { diff --git a/src/components/chatwindow.hpp b/src/components/chatwindow.hpp index 74c053f..1c0b7cc 100644 --- a/src/components/chatwindow.hpp +++ b/src/components/chatwindow.hpp @@ -39,6 +39,8 @@ public: void OpenNewTab(Snowflake id); TabsState GetTabsState(); void UseTabsState(const TabsState &state); + void GoBack(); + void GoForward(); #endif protected: diff --git a/src/windows/mainwindow.cpp b/src/windows/mainwindow.cpp index d6ca7d5..4ad694c 100644 --- a/src/windows/mainwindow.cpp +++ b/src/windows/mainwindow.cpp @@ -153,6 +153,16 @@ void MainWindow::UpdateMenus() { OnViewSubmenuPopup(); } +#ifdef WITH_LIBHANDY +void MainWindow::GoBack() { + m_chat.GoBack(); +} + +void MainWindow::GoForward() { + m_chat.GoForward(); +} +#endif + void MainWindow::OnDiscordSubmenuPopup() { auto &discord = Abaddon::Get().GetDiscordClient(); auto channel_id = GetChatActiveChannel(); diff --git a/src/windows/mainwindow.hpp b/src/windows/mainwindow.hpp index 215442c..7aec864 100644 --- a/src/windows/mainwindow.hpp +++ b/src/windows/mainwindow.hpp @@ -25,6 +25,11 @@ public: void UpdateChatReactionRemove(Snowflake id, const Glib::ustring ¶m); void UpdateMenus(); +#ifdef WITH_LIBHANDY + void GoBack(); + void GoForward(); +#endif + ChannelList *GetChannelList(); ChatWindow *GetChatWindow(); MemberList *GetMemberList();