Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion lib/ncutil/src/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,13 @@ void Config::Save(const std::string& p_Path) const

std::string Config::Get(const std::string& p_Param) const
{
return m_Map.at(p_Param);
auto it = m_Map.find(p_Param);
if (it != m_Map.end())
{
return it->second;
}
LOG_WARNING("config param \"%s\" not found, returning empty string", p_Param.c_str());
return "";
}

void Config::Set(const std::string& p_Param, const std::string& p_Value)
Expand Down
9 changes: 8 additions & 1 deletion lib/ncutil/src/fileutil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,14 @@ std::set<DirEntry, DirEntryCompare> FileUtil::ListPaths(const std::string& p_Fol
const std::vector<apathy::Path>& paths = apathy::Path::listdir(p_Folder);
for (auto& path : paths)
{
DirEntry fileinfo(path.filename(), path.is_directory() ? -1 : path.size());
std::string fullPath = p_Folder + "/" + path.filename();
struct stat st;
time_t mtime = 0;
if (stat(fullPath.c_str(), &st) == 0)
{
mtime = st.st_mtime;
}
DirEntry fileinfo(path.filename(), path.is_directory() ? -1 : path.size(), mtime);
fileinfos.insert(fileinfo);
}
return fileinfos;
Expand Down
51 changes: 50 additions & 1 deletion lib/ncutil/src/fileutil.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#pragma once

#include <ctime>
#include <set>
#include <string>
#include <vector>
Expand All @@ -18,12 +19,14 @@ struct DirEntry
DirEntry()
: name("")
, size(0)
, mtime(0)
{
}

DirEntry(const std::string& p_Name, ssize_t p_Size)
DirEntry(const std::string& p_Name, ssize_t p_Size, time_t p_Mtime = 0)
: name(p_Name)
, size(p_Size)
, mtime(p_Mtime)
{
}

Expand All @@ -39,8 +42,10 @@ struct DirEntry

std::string name;
ssize_t size = 0;
time_t mtime = 0;
};

// Default comparator: dirs first, then by name (original behavior)
struct DirEntryCompare
{
bool operator()(const DirEntry& p_Lhs, const DirEntry& p_Rhs) const
Expand All @@ -60,6 +65,50 @@ struct DirEntryCompare
}
};

// Comparator: dirs first, then by mtime (newest first)
struct DirEntryCompareDirTime
{
bool operator()(const DirEntry& p_Lhs, const DirEntry& p_Rhs) const
{
if (p_Lhs.IsHidden() != p_Rhs.IsHidden())
{
return p_Lhs.IsHidden() < p_Rhs.IsHidden();
}
else if (p_Lhs.IsDir() != p_Rhs.IsDir())
{
return p_Lhs.IsDir() > p_Rhs.IsDir();
}
else if (p_Lhs.mtime != p_Rhs.mtime)
{
return p_Lhs.mtime > p_Rhs.mtime; // newest first
}
else
{
return p_Lhs.name < p_Rhs.name;
}
}
};

// Comparator: all by mtime (newest first), dirs and files mixed
struct DirEntryCompareTime
{
bool operator()(const DirEntry& p_Lhs, const DirEntry& p_Rhs) const
{
if (p_Lhs.IsHidden() != p_Rhs.IsHidden())
{
return p_Lhs.IsHidden() < p_Rhs.IsHidden();
}
else if (p_Lhs.mtime != p_Rhs.mtime)
{
return p_Lhs.mtime > p_Rhs.mtime; // newest first
}
else
{
return p_Lhs.name < p_Rhs.name;
}
}
};

class FileUtil
{
public:
Expand Down
2 changes: 2 additions & 0 deletions src/uicolorconfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ void UiColorConfig::Init()
{ "list_color_fg", "" },
{ "list_color_unread_fg", "" },
{ "list_color_unread_bg", "" },
{ "list_color_group_fg", "" },
{ "list_color_group_unread_fg", "" },
{ "listborder_attr", "" },
{ "listborder_color_bg", "" },
{ "listborder_color_fg", "" },
Expand Down
1 change: 1 addition & 0 deletions src/uiconfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ void UiConfig::Init()
{ "failed_indicator", "\xe2\x9c\x97" },
{ "file_picker_command", "" },
{ "file_picker_persist_dir", "1" },
{ "file_picker_sort_mode", "0" },
{ "help_enabled", "1" },
{ "home_fetch_all", "0" },
{ "linefeed_on_enter", "1" },
Expand Down
58 changes: 53 additions & 5 deletions src/uifilelistdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,21 @@

#include "uifilelistdialog.h"

#include <algorithm>

#include "fileutil.h"
#include "strutil.h"
#include "uiconfig.h"
#include "uimodel.h"

UiFileListDialog::UiFileListDialog(const UiDialogParams& p_Params, const std::string& p_CurrentDir)
: UiListDialog(p_Params, true /*p_ShadeHidden*/)
, m_CurrentDir(p_CurrentDir)
{
m_Model->SetFileListDialogActive(true);
m_DirEntrys = FileUtil::ListPaths(m_CurrentDir);
std::set<DirEntry, DirEntryCompare> dirEntrys = FileUtil::ListPaths(m_CurrentDir);
m_DirEntrys = std::vector<DirEntry>(dirEntrys.begin(), dirEntrys.end());
SortDirEntrys();
UpdateList();
}

Expand All @@ -41,11 +46,13 @@ void UiFileListDialog::OnSelect()

if (m_Index < (int)m_CurrentDirEntrys.size())
{
const DirEntry& dirEntry = *std::next(m_CurrentDirEntrys.begin(), m_Index);
const DirEntry& dirEntry = m_CurrentDirEntrys[m_Index];
if (dirEntry.IsDir())
{
m_CurrentDir = FileUtil::AbsolutePath(m_CurrentDir + "/" + dirEntry.name);
m_DirEntrys = FileUtil::ListPaths(m_CurrentDir);
std::set<DirEntry, DirEntryCompare> dirEntrys = FileUtil::ListPaths(m_CurrentDir);
m_DirEntrys = std::vector<DirEntry>(dirEntrys.begin(), dirEntrys.end());
SortDirEntrys();
m_FilterStr.clear();
UpdateList();
UpdateFooter();
Expand All @@ -62,7 +69,9 @@ void UiFileListDialog::OnSelect()
void UiFileListDialog::OnBack()
{
m_CurrentDir = FileUtil::AbsolutePath(m_CurrentDir + "/..");
m_DirEntrys = FileUtil::ListPaths(m_CurrentDir);
std::set<DirEntry, DirEntryCompare> dirEntrys = FileUtil::ListPaths(m_CurrentDir);
m_DirEntrys = std::vector<DirEntry>(dirEntrys.begin(), dirEntrys.end());
SortDirEntrys();
m_FilterStr.clear();
UpdateList();
UpdateFooter();
Expand All @@ -73,6 +82,45 @@ bool UiFileListDialog::OnTimer()
return false;
}

void UiFileListDialog::SortDirEntrys()
{
int sortMode = UiConfig::GetNum("file_picker_sort_mode");

if (sortMode == 0)
{
// Default: dirs first, then by name (already sorted by ListPaths)
return;
}
else if (sortMode == 1)
{
// Dirs first, then by mtime (newest first)
std::sort(m_DirEntrys.begin(), m_DirEntrys.end(),
[](const DirEntry& a, const DirEntry& b) {
if (a.IsHidden() != b.IsHidden())
return a.IsHidden() < b.IsHidden();
else if (a.IsDir() != b.IsDir())
return a.IsDir() > b.IsDir();
else if (a.mtime != b.mtime)
return a.mtime > b.mtime; // newest first
else
return a.name < b.name;
});
}
else if (sortMode == 2)
{
// All by mtime (newest first), dirs and files mixed
std::sort(m_DirEntrys.begin(), m_DirEntrys.end(),
[](const DirEntry& a, const DirEntry& b) {
if (a.IsHidden() != b.IsHidden())
return a.IsHidden() < b.IsHidden();
else if (a.mtime != b.mtime)
return a.mtime > b.mtime; // newest first
else
return a.name < b.name;
});
}
}

void UiFileListDialog::UpdateList()
{
if (m_FilterStr.empty())
Expand All @@ -86,7 +134,7 @@ void UiFileListDialog::UpdateList()
{
if (StrUtil::ToLower(dirEntry.name).find(StrUtil::ToLower(StrUtil::ToString(m_FilterStr))) != std::string::npos)
{
m_CurrentDirEntrys.insert(dirEntry);
m_CurrentDirEntrys.push_back(dirEntry);
}
}
}
Expand Down
6 changes: 4 additions & 2 deletions src/uifilelistdialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#pragma once

#include <set>
#include <vector>

#include "uilistdialog.h"
#include "fileutil.h"
Expand All @@ -27,10 +28,11 @@ class UiFileListDialog : public UiListDialog
virtual bool OnTimer();

void UpdateList();
void SortDirEntrys();

private:
std::string m_CurrentDir;
std::set<DirEntry, DirEntryCompare> m_DirEntrys;
std::set<DirEntry, DirEntryCompare> m_CurrentDirEntrys;
std::vector<DirEntry> m_DirEntrys;
std::vector<DirEntry> m_CurrentDirEntrys;
std::string m_SelectedPath;
};
31 changes: 15 additions & 16 deletions src/uilistview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,20 +52,25 @@ void UiListView::Draw()
static int attribute = UiColorConfig::GetAttribute("list_attr");
static int attributeSelected = UiColorConfig::GetAttribute("list_attr_selected");
static int colorPairUnread = UiColorConfig::GetColorPair("list_color_unread");
static int colorPairGroup = UiColorConfig::GetColorPair("list_color_group");
static int colorPairGroupUnread = UiColorConfig::GetColorPair("list_color_group_unread");

int index = std::max(0, m_Model->GetCurrentChatIndexLocked());
const std::vector<std::pair<std::string, std::string>>& p_ChatVec = m_Model->GetChatVecLocked();

const bool emojiEnabled = m_Model->GetEmojiEnabledLocked();
std::vector<std::string> names;
std::vector<bool> unreads;
std::vector<bool> isGroups;
for (auto& chatPair : p_ChatVec)
{
const std::string& name = m_Model->GetContactListNameLocked(chatPair.first, chatPair.second, true /*p_AllowId*/,
true /*p_AllowAlias*/);
bool isUnread = m_Model->GetChatIsUnreadLocked(chatPair.first, chatPair.second);
bool isGroup = m_Model->GetChatInfoIsGroupLocked(chatPair.first, chatPair.second);
names.push_back(name);
unreads.push_back(isUnread);
isGroups.push_back(isGroup);
}

werase(m_PaddedWin);
Expand All @@ -80,12 +85,6 @@ void UiListView::Draw()
int last = std::min((height + offset), count);
for (int i = offset; i < last; ++i)
{
if (i == index)
{
wattroff(m_PaddedWin, attribute);
wattron(m_PaddedWin, attributeSelected);
}

int y = i - offset;
std::string name = names[i];
if (!emojiEnabled)
Expand All @@ -96,10 +95,17 @@ void UiListView::Draw()
std::wstring wname = StrUtil::ToWString(name).substr(0, m_PaddedW);
wname = StrUtil::TrimPadWString(wname, m_PaddedW);

int activeAttr = (i == index) ? attributeSelected : attribute;
int colorToApply = colorPair;
if (unreads[i])
{
wattron(m_PaddedWin, colorPairUnread);
colorToApply = isGroups[i] ? colorPairGroupUnread : colorPairUnread;
}
else if (isGroups[i])
{
colorToApply = colorPairGroup;
}
wattrset(m_PaddedWin, activeAttr | colorToApply);

mvwaddnwstr(m_PaddedWin, y, 0, wname.c_str(), wname.size());

Expand All @@ -108,18 +114,11 @@ void UiListView::Draw()
static const std::string unreadIndicator = " " + UiConfig::GetStr("unread_indicator");
static const std::wstring wunread = StrUtil::ToWString(unreadIndicator);
mvwaddnwstr(m_PaddedWin, y, (m_PaddedW - StrUtil::WStringWidth(wunread)), wunread.c_str(), wunread.size());

wattron(m_PaddedWin, colorPair);
}

if (i == index)
{
wattroff(m_PaddedWin, attributeSelected);
wattron(m_PaddedWin, attribute);
}
}
}

wattroff(m_PaddedWin, attribute | colorPair);
wattroff(m_PaddedWin, attribute | colorPair | colorPairGroup | colorPairGroupUnread | colorPairUnread);
wattron(m_PaddedWin, attribute | colorPair);
wrefresh(m_PaddedWin);
}
6 changes: 6 additions & 0 deletions src/uimodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4883,6 +4883,12 @@ void UiModel::SetTerminalActive(bool p_TerminalActive)
GetImpl().SetTerminalActive(p_TerminalActive);
}

bool UiModel::GetChatInfoIsGroupLocked(const std::string& p_ProfileId, const std::string& p_ChatId)
{
nc_assert(m_ModelMutex.owns_lock());
return GetImpl().GetChatInfoIsGroup(p_ProfileId, p_ChatId);
}

bool UiModel::GetChatIsUnreadLocked(const std::string& p_ProfileId, const std::string& p_ChatId)
{
nc_assert(m_ModelMutex.owns_lock());
Expand Down
1 change: 1 addition & 0 deletions src/uimodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,7 @@ class UiModel
void SetTerminalActive(bool p_TerminalActive);

// Locked methods require caller to hold model mutex (intended for Ui*View classes)
bool GetChatInfoIsGroupLocked(const std::string& p_ProfileId, const std::string& p_ChatId);
bool GetChatIsUnreadLocked(const std::string& p_ProfileId, const std::string& p_ChatId);
std::string GetChatStatusLocked(const std::string& p_ProfileId, const std::string& p_ChatId);
std::vector<std::pair<std::string, std::string>>& GetChatVecLocked();
Expand Down