Move filters and exclusions to settings
This commit is contained in:
parent
1cd369e6c0
commit
17f8016d3d
|
@ -24,7 +24,7 @@ set(DEFINITIONS "")
|
||||||
# imgui include because <GH ISSUE LINK HERE>
|
# imgui include because <GH ISSUE LINK HERE>
|
||||||
list(APPEND INCLUDES thirdparty thirdparty/imgui /usr/include/SDL2)
|
list(APPEND INCLUDES thirdparty thirdparty/imgui /usr/include/SDL2)
|
||||||
list(APPEND SOURCES main.cpp event_loop.cpp logcat_thread.cpp logcat_entry.cpp log.cpp config.cpp filters.cpp misc.cpp pcre2_wrapper.cpp
|
list(APPEND SOURCES main.cpp event_loop.cpp logcat_thread.cpp logcat_entry.cpp log.cpp config.cpp filters.cpp misc.cpp pcre2_wrapper.cpp
|
||||||
group_panel.cpp fragments/filters.cpp windows/logs.cpp windows/settings.cpp windows/filters.cpp windows/exclusions.cpp windows/main.cpp)
|
group_panel.cpp fragments/filters.cpp windows/logs.cpp windows/settings.cpp windows/main.cpp)
|
||||||
list(APPEND IMGUI_SOURCES thirdparty/imgui/imgui.cpp thirdparty/imgui/imgui_draw.cpp thirdparty/imgui/imgui_widgets.cpp thirdparty/imgui/imgui_tables.cpp
|
list(APPEND IMGUI_SOURCES thirdparty/imgui/imgui.cpp thirdparty/imgui/imgui_draw.cpp thirdparty/imgui/imgui_widgets.cpp thirdparty/imgui/imgui_tables.cpp
|
||||||
thirdparty/imgui/misc/cpp/imgui_stdlib.cpp thirdparty/imgui/misc/freetype/imgui_freetype.cpp
|
thirdparty/imgui/misc/cpp/imgui_stdlib.cpp thirdparty/imgui/misc/freetype/imgui_freetype.cpp
|
||||||
thirdparty/imgui/backends/imgui_impl_sdl2.cpp thirdparty/imgui/backends/imgui_impl_opengl3.cpp)
|
thirdparty/imgui/backends/imgui_impl_sdl2.cpp thirdparty/imgui/backends/imgui_impl_opengl3.cpp)
|
||||||
|
|
|
@ -6,8 +6,6 @@
|
||||||
#include "logcat_thread.h"
|
#include "logcat_thread.h"
|
||||||
|
|
||||||
#include "windows/logs.h"
|
#include "windows/logs.h"
|
||||||
#include "windows/filters.h"
|
|
||||||
#include "windows/exclusions.h"
|
|
||||||
#include "windows/settings.h"
|
#include "windows/settings.h"
|
||||||
#include "windows/main.h"
|
#include "windows/main.h"
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
|
@ -37,8 +35,6 @@ static inline void check_for_logcat_items(LogcatThread& logcat_thread, const Con
|
||||||
void event_loop(ImFont* monospace_font, Config& active_config, LogcatThread& logcat_thread) {
|
void event_loop(ImFont* monospace_font, Config& active_config, LogcatThread& logcat_thread) {
|
||||||
static Config inactive_config;
|
static Config inactive_config;
|
||||||
static bool show_settings_window = false;
|
static bool show_settings_window = false;
|
||||||
static bool show_filters_window = false;
|
|
||||||
static bool show_exclusions_window = false;
|
|
||||||
static bool show_logs_window = false;
|
static bool show_logs_window = false;
|
||||||
static size_t log_entries_read = 0;
|
static size_t log_entries_read = 0;
|
||||||
static std::vector<LogcatEntry> logcat_entries;
|
static std::vector<LogcatEntry> logcat_entries;
|
||||||
|
@ -51,14 +47,7 @@ void event_loop(ImFont* monospace_font, Config& active_config, LogcatThread& log
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (show_settings_window) {
|
if (show_settings_window) {
|
||||||
settings_window(monospace_font, active_config, inactive_config, &show_settings_window);
|
settings_window(monospace_font, active_config, inactive_config, logcat_entries, filtered_logcat_entry_offsets, &show_settings_window);
|
||||||
}
|
|
||||||
|
|
||||||
if (show_filters_window) {
|
|
||||||
filters_window(active_config, inactive_config, logcat_entries, filtered_logcat_entry_offsets, &show_filters_window);
|
|
||||||
}
|
|
||||||
if (show_exclusions_window) {
|
|
||||||
exclusions_window(active_config, inactive_config, logcat_entries, filtered_logcat_entry_offsets, &show_exclusions_window);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (show_logs_window) {
|
if (show_logs_window) {
|
||||||
|
@ -73,5 +62,5 @@ void event_loop(ImFont* monospace_font, Config& active_config, LogcatThread& log
|
||||||
main_window(log_entries_read == log_entries.size(), monospace_font, logcat_thread,
|
main_window(log_entries_read == log_entries.size(), monospace_font, logcat_thread,
|
||||||
logcat_entries, filtered_logcat_entry_offsets,
|
logcat_entries, filtered_logcat_entry_offsets,
|
||||||
active_config, inactive_config,
|
active_config, inactive_config,
|
||||||
&show_settings_window, &show_filters_window, &show_exclusions_window, &show_logs_window);
|
&show_settings_window, &show_logs_window);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
#include "../group_panel.h"
|
#include "../group_panel.h"
|
||||||
#include "../filters.h"
|
#include "../filters.h"
|
||||||
#include "../config.h"
|
|
||||||
#include "ok_buttons_fragment.h"
|
#include "ok_buttons_fragment.h"
|
||||||
#include "filters.h"
|
#include "filters.h"
|
||||||
|
|
||||||
|
@ -13,9 +12,6 @@ static inline void render_buffer_filter(BufferFilter* filter);
|
||||||
static inline void render_priority_filter(PriorityFilter* filter);
|
static inline void render_priority_filter(PriorityFilter* filter);
|
||||||
static inline void render_group_filter(GroupFilter* filter);
|
static inline void render_group_filter(GroupFilter* filter);
|
||||||
static std::unique_ptr<Filter> render_add_filter_popup();
|
static std::unique_ptr<Filter> render_add_filter_popup();
|
||||||
static void update_logcat_entries(const Config& active_config,
|
|
||||||
const std::vector<LogcatEntry>& logcat_entries, std::vector<size_t>& filtered_logcat_entry_offsets);
|
|
||||||
static void try_write_config(const Config& config);
|
|
||||||
|
|
||||||
static void render_filter(Filter* filter, std::string* title, bool* request_removal) {
|
static void render_filter(Filter* filter, std::string* title, bool* request_removal) {
|
||||||
ImGui::PushID(filter);
|
ImGui::PushID(filter);
|
||||||
|
@ -210,27 +206,7 @@ static std::unique_ptr<Filter> render_add_filter_popup() {
|
||||||
return std::unique_ptr<Filter>();
|
return std::unique_ptr<Filter>();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void update_logcat_entries(const Config& active_config,
|
void filters_fragment(Filters& inactive_filters) {
|
||||||
const std::vector<LogcatEntry>& logcat_entries, std::vector<size_t>& filtered_logcat_entry_offsets) {
|
|
||||||
filtered_logcat_entry_offsets.clear();
|
|
||||||
for (size_t i=0; i < logcat_entries.size(); i++) {
|
|
||||||
if (matches(logcat_entries[i], active_config.filters, active_config.exclusions)) {
|
|
||||||
filtered_logcat_entry_offsets.push_back(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void try_write_config(const Config& config) {
|
|
||||||
try {
|
|
||||||
write_config(config);
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
log(std::string("Failed to write config: ") + e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void filters_fragment(Config& active_config, Filters& __restrict active_filters, Filters& __restrict inactive_filters,
|
|
||||||
const std::vector<LogcatEntry>& logcat_entries, std::vector<size_t>& filtered_logcat_entry_offsets,
|
|
||||||
bool* p_open) {
|
|
||||||
ImGui::TextWrapped("You can use regex for strings by prepending \"regex:\". "
|
ImGui::TextWrapped("You can use regex for strings by prepending \"regex:\". "
|
||||||
"While editing titles, press Enter to use the new title or press Escape to keep the old one");
|
"While editing titles, press Enter to use the new title or press Escape to keep the old one");
|
||||||
|
|
||||||
|
@ -254,15 +230,4 @@ void filters_fragment(Config& active_config, Filters& __restrict active_filters,
|
||||||
}
|
}
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::Separator();
|
|
||||||
ok_buttons_fragment(p_open, [&]() {
|
|
||||||
active_filters = std::move(inactive_filters);
|
|
||||||
try_write_config(active_config);
|
|
||||||
update_logcat_entries(active_config, logcat_entries, filtered_logcat_entry_offsets);
|
|
||||||
}, [&]() {
|
|
||||||
copy_filters(active_filters, inactive_filters);
|
|
||||||
try_write_config(active_config);
|
|
||||||
update_logcat_entries(active_config, logcat_entries, filtered_logcat_entry_offsets);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
struct Config; // forward declaration from ../config.h
|
|
||||||
#include "../filters.h"
|
#include "../filters.h"
|
||||||
|
|
||||||
void filters_fragment(Config& active_config, Filters& __restrict active_filters, Filters& __restrict inactive_filters,
|
void filters_fragment(Filters& inactive_filters);
|
||||||
const std::vector<LogcatEntry>& logcat_entries, std::vector<size_t>& filtered_logcat_entry_offsets,
|
|
||||||
bool* p_open);
|
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
#include <imgui/imgui.h>
|
|
||||||
|
|
||||||
#include "../misc.h"
|
|
||||||
#include "../fragments/filters.h"
|
|
||||||
#include "../filters.h"
|
|
||||||
#include "../config.h"
|
|
||||||
#include "exclusions.h"
|
|
||||||
|
|
||||||
void exclusions_window(Config& __restrict active_config, Config& __restrict inactive_config,
|
|
||||||
const std::vector<LogcatEntry>& logcat_entries, std::vector<size_t>& filtered_logcat_entry_offsets,
|
|
||||||
bool* p_open) {
|
|
||||||
if (!ImGui::BeginWithCloseShortcut("Exclusions", p_open)) {
|
|
||||||
ImGui::End();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
filters_fragment(active_config, active_config.exclusions, inactive_config.exclusions, logcat_entries, filtered_logcat_entry_offsets, p_open);
|
|
||||||
|
|
||||||
ImGui::End();
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
struct Config; // forward declaration from ../config.h
|
|
||||||
#include "../logcat_entry.h"
|
|
||||||
|
|
||||||
void exclusions_window(Config& __restrict active_config, Config& __restrict inactive_config,
|
|
||||||
const std::vector<LogcatEntry>& logcat_entries, std::vector<size_t>& filtered_logcat_entry_offsets,
|
|
||||||
bool* p_open);
|
|
|
@ -1,20 +0,0 @@
|
||||||
#include <imgui/imgui.h>
|
|
||||||
|
|
||||||
#include "../misc.h"
|
|
||||||
#include "../fragments/filters.h"
|
|
||||||
#include "../filters.h"
|
|
||||||
#include "../config.h"
|
|
||||||
#include "filters.h"
|
|
||||||
|
|
||||||
void filters_window(Config& __restrict active_config, Config& __restrict inactive_config,
|
|
||||||
const std::vector<LogcatEntry>& logcat_entries, std::vector<size_t>& filtered_logcat_entry_offsets,
|
|
||||||
bool* p_open) {
|
|
||||||
if (!ImGui::BeginWithCloseShortcut("Filters", p_open)) {
|
|
||||||
ImGui::End();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
filters_fragment(active_config, active_config.filters, inactive_config.filters, logcat_entries, filtered_logcat_entry_offsets, p_open);
|
|
||||||
|
|
||||||
ImGui::End();
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
struct Config; // forward declaration from ../config.h
|
|
||||||
#include "../logcat_entry.h"
|
|
||||||
|
|
||||||
void filters_window(Config& __restrict active_config, Config& __restrict inactive_config,
|
|
||||||
const std::vector<LogcatEntry>& logcat_entries, std::vector<size_t>& filtered_logcat_entry_offsets,
|
|
||||||
bool* p_open);
|
|
|
@ -110,7 +110,7 @@ static inline void render_table(ImFont* monospace_font, std::vector<LogcatEntry>
|
||||||
void main_window(bool latest_log_entries_read, ImFont* monospace_font, LogcatThread& logcat_thread,
|
void main_window(bool latest_log_entries_read, ImFont* monospace_font, LogcatThread& logcat_thread,
|
||||||
std::vector<LogcatEntry>& logcat_entries, std::vector<size_t>& filtered_logcat_entry_offsets,
|
std::vector<LogcatEntry>& logcat_entries, std::vector<size_t>& filtered_logcat_entry_offsets,
|
||||||
const Config& __restrict active_config, Config& __restrict inactive_config,
|
const Config& __restrict active_config, Config& __restrict inactive_config,
|
||||||
bool* __restrict show_settings_window, bool* __restrict show_filters_window, bool* __restrict show_exclusions_window, bool* __restrict show_logs_window) {
|
bool* __restrict show_settings_window, bool* __restrict show_logs_window) {
|
||||||
|
|
||||||
ImGui::SetNextWindowPos(ImGui::GetMainViewport()->WorkPos);
|
ImGui::SetNextWindowPos(ImGui::GetMainViewport()->WorkPos);
|
||||||
ImGui::SetNextWindowSize(ImGui::GetMainViewport()->WorkSize);
|
ImGui::SetNextWindowSize(ImGui::GetMainViewport()->WorkSize);
|
||||||
|
@ -124,17 +124,9 @@ void main_window(bool latest_log_entries_read, ImFont* monospace_font, LogcatThr
|
||||||
inactive_config.logcat_command = active_config.logcat_command;
|
inactive_config.logcat_command = active_config.logcat_command;
|
||||||
inactive_config.normal_font_size = active_config.normal_font_size;
|
inactive_config.normal_font_size = active_config.normal_font_size;
|
||||||
inactive_config.monospace_font_size = active_config.monospace_font_size;
|
inactive_config.monospace_font_size = active_config.monospace_font_size;
|
||||||
*show_settings_window = true;
|
|
||||||
}
|
|
||||||
ImGui::SameLine();
|
|
||||||
if (ImGui::Button("Filters") && !*show_filters_window) {
|
|
||||||
copy_filters(inactive_config.filters, active_config.filters);
|
copy_filters(inactive_config.filters, active_config.filters);
|
||||||
*show_filters_window = true;
|
|
||||||
}
|
|
||||||
ImGui::SameLine();
|
|
||||||
if (ImGui::Button("Exclusions") && !*show_exclusions_window) {
|
|
||||||
copy_filters(inactive_config.exclusions, active_config.exclusions);
|
copy_filters(inactive_config.exclusions, active_config.exclusions);
|
||||||
*show_exclusions_window = true;
|
*show_settings_window = true;
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
bool open_logs = latest_log_entries_read ? ImGui::Button("Logs") : ImGui::RedButton("Logs");
|
bool open_logs = latest_log_entries_read ? ImGui::Button("Logs") : ImGui::RedButton("Logs");
|
||||||
|
|
|
@ -10,4 +10,4 @@ class LogcatThread; // forward declaration from ../logcat_thread.h
|
||||||
void main_window(bool latest_log_entries_read, ImFont* monospace_font, LogcatThread& logcat_thread,
|
void main_window(bool latest_log_entries_read, ImFont* monospace_font, LogcatThread& logcat_thread,
|
||||||
std::vector<LogcatEntry>& logcat_entries, std::vector<size_t>& filtered_logcat_entry_offsets,
|
std::vector<LogcatEntry>& logcat_entries, std::vector<size_t>& filtered_logcat_entry_offsets,
|
||||||
const Config& __restrict active_config, Config& __restrict inactive_config,
|
const Config& __restrict active_config, Config& __restrict inactive_config,
|
||||||
bool* __restrict show_settings_window, bool* __restrict show_filters_window, bool* __restrict show_exclusions_window, bool* __restrict show_logs_window);
|
bool* __restrict show_settings_window, bool* __restrict show_logs_window);
|
||||||
|
|
|
@ -3,9 +3,20 @@
|
||||||
|
|
||||||
#include "../misc.h"
|
#include "../misc.h"
|
||||||
#include "../config.h"
|
#include "../config.h"
|
||||||
|
#include "../fragments/filters.h"
|
||||||
#include "../fragments/ok_buttons_fragment.h"
|
#include "../fragments/ok_buttons_fragment.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
|
||||||
|
static void update_logcat_entries(const Config& active_config,
|
||||||
|
const std::vector<LogcatEntry>& logcat_entries, std::vector<size_t>& filtered_logcat_entry_offsets) {
|
||||||
|
filtered_logcat_entry_offsets.clear();
|
||||||
|
for (size_t i=0; i < logcat_entries.size(); i++) {
|
||||||
|
if (matches(logcat_entries[i], active_config.filters, active_config.exclusions)) {
|
||||||
|
filtered_logcat_entry_offsets.push_back(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void try_write_config(const Config& config) {
|
static void try_write_config(const Config& config) {
|
||||||
try {
|
try {
|
||||||
write_config(config);
|
write_config(config);
|
||||||
|
@ -14,11 +25,7 @@ static void try_write_config(const Config& config) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void settings_window(ImFont* monospace_font, Config& __restrict active_config, Config& __restrict inactive_config, bool* p_open) {
|
static inline void settings_fragment(ImFont* monospace_font, Config& inactive_config) {
|
||||||
if (!ImGui::BeginWithCloseShortcut("Settings", p_open)) {
|
|
||||||
ImGui::End();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ImGui::TextUnformatted("Logcat command only takes effect when logcat is not running");
|
ImGui::TextUnformatted("Logcat command only takes effect when logcat is not running");
|
||||||
ImGui::PushFont(monospace_font);
|
ImGui::PushFont(monospace_font);
|
||||||
ImGui::InputTextWithHint("##logcat_command", default_logcat_command, &inactive_config.logcat_command);
|
ImGui::InputTextWithHint("##logcat_command", default_logcat_command, &inactive_config.logcat_command);
|
||||||
|
@ -31,17 +38,44 @@ void settings_window(ImFont* monospace_font, Config& __restrict active_config, C
|
||||||
ImGui::InputFloat("Normal font size", &inactive_config.normal_font_size, 0.5f, 1.0f, "%.3f");
|
ImGui::InputFloat("Normal font size", &inactive_config.normal_font_size, 0.5f, 1.0f, "%.3f");
|
||||||
#endif
|
#endif
|
||||||
ImGui::InputFloat("Monospace font size", &inactive_config.monospace_font_size, 0.5f, 1.0f, "%.3f");
|
ImGui::InputFloat("Monospace font size", &inactive_config.monospace_font_size, 0.5f, 1.0f, "%.3f");
|
||||||
|
}
|
||||||
|
|
||||||
|
void settings_window(ImFont* monospace_font, Config& __restrict active_config, Config& __restrict inactive_config,
|
||||||
|
const std::vector<LogcatEntry>& logcat_entries, std::vector<size_t>& filtered_logcat_entry_offsets,
|
||||||
|
bool* p_open) {
|
||||||
|
if (!ImGui::BeginWithCloseShortcut("Settings", p_open)) {
|
||||||
|
ImGui::End();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::BeginTabBar("settings")) {
|
||||||
|
if (ImGui::BeginTabItem("Settings")) {
|
||||||
|
settings_fragment(monospace_font, inactive_config);
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
|
if (ImGui::BeginTabItem("Filters")) {
|
||||||
|
filters_fragment(inactive_config.filters);
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
|
if (ImGui::BeginTabItem("Exclusions")) {
|
||||||
|
filters_fragment(inactive_config.exclusions);
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
|
ImGui::EndTabBar();
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ok_buttons_fragment(p_open, [&]() {
|
ok_buttons_fragment(p_open, [&]() {
|
||||||
active_config.logcat_command = std::move(inactive_config.logcat_command);
|
active_config = std::move(inactive_config);
|
||||||
active_config.normal_font_size = inactive_config.normal_font_size;
|
update_logcat_entries(active_config, logcat_entries, filtered_logcat_entry_offsets);
|
||||||
active_config.monospace_font_size = inactive_config.monospace_font_size;
|
|
||||||
try_write_config(active_config);
|
try_write_config(active_config);
|
||||||
}, [&]() {
|
}, [&]() {
|
||||||
active_config.logcat_command = inactive_config.logcat_command;
|
active_config.logcat_command = inactive_config.logcat_command;
|
||||||
active_config.normal_font_size = inactive_config.normal_font_size;
|
active_config.normal_font_size = inactive_config.normal_font_size;
|
||||||
active_config.monospace_font_size = inactive_config.monospace_font_size;
|
active_config.monospace_font_size = inactive_config.monospace_font_size;
|
||||||
|
copy_filters(active_config.filters, inactive_config.filters);
|
||||||
|
copy_filters(active_config.exclusions, inactive_config.exclusions);
|
||||||
|
update_logcat_entries(active_config, logcat_entries, filtered_logcat_entry_offsets);
|
||||||
try_write_config(active_config);
|
try_write_config(active_config);
|
||||||
});
|
});
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
struct ImFont; // forward declaration from imgui/imgui.h
|
struct ImFont; // forward declaration from imgui/imgui.h
|
||||||
|
|
||||||
struct Config; // forward declaration from ../config.h
|
struct Config; // forward declaration from ../config.h
|
||||||
|
struct LogcatEntry; // forward declaration from ../logcat_entry.h
|
||||||
|
|
||||||
void settings_window(ImFont* monospace_font, Config& __restrict active_config, Config& __restrict inactive_config, bool* p_open);
|
void settings_window(ImFont* monospace_font, Config& __restrict active_config, Config& __restrict inactive_config,
|
||||||
|
const std::vector<LogcatEntry>& logcat_entries, std::vector<size_t>& filtered_logcat_entry_offsets,
|
||||||
|
bool* p_open);
|
||||||
|
|
Loading…
Reference in New Issue