diff --git a/AK/AKString.h b/AK/AKString.h index 59c81188ddc..31b2b4d383f 100644 --- a/AK/AKString.h +++ b/AK/AKString.h @@ -76,6 +76,7 @@ public: bool operator==(const String&) const; bool operator!=(const String& other) const { return !(*this == other); } + bool operator<(const String&) const; String isolated_copy() const; diff --git a/AK/String.cpp b/AK/String.cpp index 4c3e2692acb..fe48dc004c9 100644 --- a/AK/String.cpp +++ b/AK/String.cpp @@ -19,6 +19,17 @@ bool String::operator==(const String& other) const return !memcmp(characters(), other.characters(), length()); } +bool String::operator<(const String& other) const +{ + if (!m_impl) + return other.m_impl; + + if (!other.m_impl) + return false; + + return strcmp(characters(), other.characters()); +} + String String::empty() { return StringImpl::the_empty_stringimpl(); diff --git a/Applications/ProcessManager/ProcessTableModel.cpp b/Applications/ProcessManager/ProcessTableModel.cpp index 3b8e1b05d9b..fbf530d7376 100644 --- a/Applications/ProcessManager/ProcessTableModel.cpp +++ b/Applications/ProcessManager/ProcessTableModel.cpp @@ -3,19 +3,6 @@ #include #include -enum Column { - Icon = 0, - Name, - CPU, - State, - Priority, - User, - PID, - Linear, - Physical, - __Count -}; - ProcessTableModel::ProcessTableModel() { setpwent(); diff --git a/Applications/ProcessManager/ProcessTableModel.h b/Applications/ProcessManager/ProcessTableModel.h index 35b1145a372..6b5d9af65b6 100644 --- a/Applications/ProcessManager/ProcessTableModel.h +++ b/Applications/ProcessManager/ProcessTableModel.h @@ -8,6 +8,19 @@ class ProcessTableModel final : public GTableModel { public: + enum Column { + Icon = 0, + Name, + CPU, + State, + Priority, + User, + PID, + Linear, + Physical, + __Count + }; + ProcessTableModel(); virtual ~ProcessTableModel() override; diff --git a/Applications/ProcessManager/ProcessTableView.cpp b/Applications/ProcessManager/ProcessTableView.cpp index 015f1b598dc..8dd937223e8 100644 --- a/Applications/ProcessManager/ProcessTableView.cpp +++ b/Applications/ProcessManager/ProcessTableView.cpp @@ -1,11 +1,15 @@ #include "ProcessTableView.h" #include "ProcessTableModel.h" - +#include +#include ProcessTableView::ProcessTableView(GWidget* parent) : GTableView(parent) { - set_model(make()); + auto process_model = make(); + m_model = process_model.ptr(); + set_model(make(move(process_model))); + GTableView::model()->set_key_column_and_sort_order(ProcessTableModel::Column::CPU, GSortOrder::Descending); start_timer(1000); model().update(); } @@ -32,13 +36,3 @@ pid_t ProcessTableView::selected_pid() const { return model().selected_pid(); } - -inline ProcessTableModel& ProcessTableView::model() -{ - return static_cast(*GTableView::model()); -} - -inline const ProcessTableModel& ProcessTableView::model() const -{ - return static_cast(*GTableView::model()); -} diff --git a/Applications/ProcessManager/ProcessTableView.h b/Applications/ProcessManager/ProcessTableView.h index bd0fe615b46..8c5f37b5528 100644 --- a/Applications/ProcessManager/ProcessTableView.h +++ b/Applications/ProcessManager/ProcessTableView.h @@ -21,7 +21,9 @@ protected: private: virtual void timer_event(GTimerEvent&) override; - ProcessTableModel& model(); - const ProcessTableModel& model() const; + ProcessTableModel& model() { return *m_model; } + const ProcessTableModel& model() const { return *m_model; } + + ProcessTableModel* m_model { nullptr }; }; diff --git a/Kernel/init.cpp b/Kernel/init.cpp index f55caa81b87..b6827b0e107 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -27,8 +27,8 @@ #define SPAWN_LAUNCHER //#define SPAWN_GUITEST2 //#define SPAWN_FILE_MANAGER -//#define SPAWN_PROCESS_MANAGER -#define SPAWN_TEXT_EDITOR +#define SPAWN_PROCESS_MANAGER +//#define SPAWN_TEXT_EDITOR //#define SPAWN_FONTEDITOR //#define SPAWN_MULTIPLE_SHELLS //#define STRESS_TEST_SPAWNING diff --git a/LibC/qsort.cpp b/LibC/qsort.cpp index 0f5aa398e4d..3bf55bc6394 100644 --- a/LibC/qsort.cpp +++ b/LibC/qsort.cpp @@ -38,7 +38,8 @@ static char sccsid[] = "@(#)qsort.c 5.9 (Berkeley) 2/23/91"; #include #include -static void insertion_sort(void* bot, size_t nmemb, size_t size, int (*compar)(const void *, const void *)); +static void insertion_sort(void* bot, size_t nmemb, size_t size, int (*compar)(const void*, const void*)); +static void insertion_sort_r(void* bot, size_t nmemb, size_t size, int (*compar)(const void*, const void*, void*), void* arg); void qsort(void* bot, size_t nmemb, size_t size, int (*compar)(const void *, const void *)) { @@ -48,23 +49,23 @@ void qsort(void* bot, size_t nmemb, size_t size, int (*compar)(const void *, con insertion_sort(bot, nmemb, size, compar); } -void insertion_sort(void* bot, size_t nmemb, size_t size, int (*compar)(const void *, const void *)) +void qsort_r(void* bot, size_t nmemb, size_t size, int (*compar)(const void*, const void*, void*), void* arg) +{ + if (nmemb <= 1) + return; + + insertion_sort_r(bot, nmemb, size, compar, arg); +} + +void insertion_sort(void* bot, size_t nmemb, size_t size, int (*compar)(const void*, const void*)) { int cnt; unsigned char ch; char *s1, *s2, *t1, *t2, *top; - - /* - * A simple insertion sort (see Knuth, Vol. 3, page 81, Algorithm - * S). Insertion sort has the same worst case as most simple sorts - * (O N^2). It gets used here because it is (O N) in the case of - * sorted data. - */ top = (char*)bot + nmemb * size; for (t1 = (char*)bot + size; t1 < top;) { for (t2 = t1; (t2 -= size) >= bot && compar(t1, t2) < 0;); if (t1 != (t2 += size)) { - /* Bubble bytes up through each element. */ for (cnt = size; cnt--; ++t1) { ch = *t1; for (s1 = s2 = t1; (s2 -= size) >= t2; s1 = s2) @@ -75,3 +76,23 @@ void insertion_sort(void* bot, size_t nmemb, size_t size, int (*compar)(const vo t1 += size; } } + +void insertion_sort_r(void* bot, size_t nmemb, size_t size, int (*compar)(const void*, const void*, void*), void* arg) +{ + int cnt; + unsigned char ch; + char *s1, *s2, *t1, *t2, *top; + top = (char*)bot + nmemb * size; + for (t1 = (char*)bot + size; t1 < top;) { + for (t2 = t1; (t2 -= size) >= bot && compar(t1, t2, arg) < 0;); + if (t1 != (t2 += size)) { + for (cnt = size; cnt--; ++t1) { + ch = *t1; + for (s1 = s2 = t1; (s2 -= size) >= t2; s1 = s2) + *s1 = *s2; + *s1 = ch; + } + } else + t1 += size; + } +} diff --git a/LibC/stdlib.h b/LibC/stdlib.h index 007ef9ca684..a5d2069ba10 100644 --- a/LibC/stdlib.h +++ b/LibC/stdlib.h @@ -21,7 +21,8 @@ long atol(const char*); double strtod(const char*, char** endptr); long strtol(const char*, char** endptr, int base); unsigned long strtoul(const char*, char** endptr, int base); -void qsort(void* base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)); +void qsort(void* base, size_t nmemb, size_t size, int (*compar)(const void*, const void*)); +void qsort_r(void* base, size_t nmemb, size_t size, int (*compar)(const void*, const void*, void*), void* arg); int atexit(void (*function)()); __attribute__((noreturn)) void exit(int status); __attribute__((noreturn)) void abort(); diff --git a/LibGUI/GSortingProxyTableModel.cpp b/LibGUI/GSortingProxyTableModel.cpp new file mode 100644 index 00000000000..2c02e123e2d --- /dev/null +++ b/LibGUI/GSortingProxyTableModel.cpp @@ -0,0 +1,116 @@ +#include +#include +#include + +GSortingProxyTableModel::GSortingProxyTableModel(OwnPtr&& target) + : m_target(move(target)) + , m_key_column(-1) +{ + m_target->on_model_update = [this] (GTableModel&) { + resort(); + }; +} + +GSortingProxyTableModel::~GSortingProxyTableModel() +{ +} + +int GSortingProxyTableModel::row_count() const +{ + return target().row_count(); +} + +int GSortingProxyTableModel::column_count() const +{ + return target().column_count(); +} + +GModelIndex GSortingProxyTableModel::map_to_target(const GModelIndex& index) const +{ + ASSERT(!m_row_mappings.is_empty()); + if (!index.is_valid()) { + ASSERT_NOT_REACHED(); + return { }; + } + if (index.row() >= row_count() || index.column() >= column_count()) { + ASSERT_NOT_REACHED(); + return { }; + } + return { m_row_mappings[index.row()], index.column() }; +} + +String GSortingProxyTableModel::row_name(int index) const +{ + return target().row_name(index); +} + +String GSortingProxyTableModel::column_name(int index) const +{ + return target().column_name(index); +} + +GTableModel::ColumnMetadata GSortingProxyTableModel::column_metadata(int index) const +{ + return target().column_metadata(index); +} + +GVariant GSortingProxyTableModel::data(const GModelIndex& index) const +{ + return target().data(map_to_target(index)); +} + +void GSortingProxyTableModel::activate(const GModelIndex& index) +{ + target().activate(map_to_target(index)); +} + +void GSortingProxyTableModel::update() +{ + target().update(); +} + +void GSortingProxyTableModel::set_key_column_and_sort_order(int column, GSortOrder sort_order) +{ + if (column == m_key_column && sort_order == m_sort_order) + return; + + ASSERT(column >= 0 && column < column_count()); + m_key_column = column; + m_sort_order = sort_order; + resort(); +} + +void GSortingProxyTableModel::resort() +{ + int row_count = target().row_count(); + m_row_mappings.resize(row_count); + for (int i = 0; i < row_count; ++i) + m_row_mappings[i] = i; + + if (m_key_column == -1) + return; + + struct Context { + GTableModel* target; + int key_column; + GSortOrder sort_order; + }; + Context context { m_target.ptr(), m_key_column, m_sort_order }; + qsort_r(m_row_mappings.data(), m_row_mappings.size(), sizeof(int), [] (const void* a, const void* b, void* ctx) -> int { + int row1 = *(const int*)(a); + int row2 = *(const int*)(b); + auto& context = *(Context*)(ctx); + GModelIndex index1 { row1, context.key_column }; + GModelIndex index2 { row2, context.key_column }; + auto data1 = context.target->data(index1); + auto data2 = context.target->data(index2); + if (data1 == data2) + return 0; + bool is_less_than = data1 < data2; + if (context.sort_order == GSortOrder::Ascending) + return is_less_than ? -1 : 1; + return is_less_than ? 1 : -1; + }, &context); + + did_update(); +} diff --git a/LibGUI/GSortingProxyTableModel.h b/LibGUI/GSortingProxyTableModel.h new file mode 100644 index 00000000000..a920edddde2 --- /dev/null +++ b/LibGUI/GSortingProxyTableModel.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +class GSortingProxyTableModel final : public GTableModel { +public: + explicit GSortingProxyTableModel(OwnPtr&&); + virtual ~GSortingProxyTableModel() override; + + virtual int row_count() const override; + virtual int column_count() const override; + virtual String row_name(int) const override; + virtual String column_name(int) const override; + virtual ColumnMetadata column_metadata(int) const override; + virtual GVariant data(const GModelIndex&) const override; + virtual void update() override; + virtual void activate(const GModelIndex&) override; + + virtual int key_column() const override { return m_key_column; } + virtual GSortOrder sort_order() const override { return m_sort_order; } + virtual void set_key_column_and_sort_order(int, GSortOrder) override; + + GModelIndex map_to_target(const GModelIndex&) const; + +private: + GTableModel& target() { return *m_target; } + const GTableModel& target() const { return *m_target; } + + void resort(); + + OwnPtr m_target; + Vector m_row_mappings; + int m_key_column { -1 }; + GSortOrder m_sort_order { GSortOrder::Ascending }; +}; diff --git a/LibGUI/GTableModel.cpp b/LibGUI/GTableModel.cpp index c45d615558e..e9185199b82 100644 --- a/LibGUI/GTableModel.cpp +++ b/LibGUI/GTableModel.cpp @@ -27,6 +27,8 @@ void GTableModel::for_each_view(Function callback) void GTableModel::did_update() { + if (on_model_update) + on_model_update(*this); for_each_view([] (GTableView& view) { view.did_update_model(); }); diff --git a/LibGUI/GTableModel.h b/LibGUI/GTableModel.h index e0b43b904a6..517e682b11a 100644 --- a/LibGUI/GTableModel.h +++ b/LibGUI/GTableModel.h @@ -10,6 +10,8 @@ class GTableView; +enum class GSortOrder { None, Ascending, Descending }; + class GModelNotification { public: enum Type { @@ -57,9 +59,15 @@ public: void set_selected_index(const GModelIndex& index) { m_selected_index = index; } GModelIndex selected_index() const { return m_selected_index; } + virtual int key_column() const { return -1; } + virtual GSortOrder sort_order() const { return GSortOrder::None; } + virtual void set_key_column_and_sort_order(int, GSortOrder) { } + void register_view(Badge, GTableView&); void unregister_view(Badge, GTableView&); + Function on_model_update; + protected: GTableModel(); diff --git a/LibGUI/GVariant.cpp b/LibGUI/GVariant.cpp index 3592759807f..7def7758851 100644 --- a/LibGUI/GVariant.cpp +++ b/LibGUI/GVariant.cpp @@ -48,6 +48,49 @@ GVariant::GVariant(const GraphicsBitmap& value) AK::retain_if_not_null(m_value.as_bitmap); } +bool GVariant::operator==(const GVariant& other) const +{ + if (m_type != other.m_type) + return to_string() == other.to_string(); + switch (m_type) { + case Type::Bool: + return as_bool() == other.as_bool(); + case Type::Int: + return as_int() == other.as_int(); + case Type::Float: + return as_float() == other.as_float(); + case Type::String: + return as_string() == other.as_string(); + case Type::Bitmap: + return m_value.as_bitmap == other.m_value.as_bitmap; + case Type::Invalid: + break; + } + ASSERT_NOT_REACHED(); +} + +bool GVariant::operator<(const GVariant& other) const +{ + if (m_type != other.m_type) + return to_string() < other.to_string(); + switch (m_type) { + case Type::Bool: + return as_bool() < other.as_bool(); + case Type::Int: + return as_int() < other.as_int(); + case Type::Float: + return as_float() < other.as_float(); + case Type::String: + return as_string() < other.as_string(); + case Type::Bitmap: + // FIXME: Maybe compare bitmaps somehow differently? + return m_value.as_bitmap < other.m_value.as_bitmap; + case Type::Invalid: + break; + } + ASSERT_NOT_REACHED(); +} + String GVariant::to_string() const { switch (m_type) { diff --git a/LibGUI/GVariant.h b/LibGUI/GVariant.h index 1dd6faef12b..87c7f27175f 100644 --- a/LibGUI/GVariant.h +++ b/LibGUI/GVariant.h @@ -62,6 +62,9 @@ public: String to_string() const; + bool operator==(const GVariant&) const; + bool operator<(const GVariant&) const; + private: union { StringImpl* as_string; diff --git a/LibGUI/Makefile b/LibGUI/Makefile index 09acca6aad9..e3f18f61f1e 100644 --- a/LibGUI/Makefile +++ b/LibGUI/Makefile @@ -34,6 +34,7 @@ LIBGUI_OBJS = \ GShortcut.o \ GTextEditor.o \ GClipboard.o \ + GSortingProxyTableModel.o \ GWindow.o OBJS = $(SHAREDGRAPHICS_OBJS) $(LIBGUI_OBJS)