aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorRobin Haberkorn <rhaberkorn@fmsbw.de>2025-12-23 13:54:17 +0100
committerRobin Haberkorn <rhaberkorn@fmsbw.de>2025-12-23 13:54:17 +0100
commit0c89fb700957e411885e7e7835e15f441e8b5e84 (patch)
tree56d756d6fa1923a5198504a322ec9c67cf274922 /src
parent2592ef74ab2eba57c32fe21993ce01e9698b106f (diff)
fixed clicking the "(Unnamed)" buffer in 0EB popupsHEADmaster-fmsbw-cimaster
* When constructing the list of popup items, the unnamed buffer is stored as the empty string instead of a prerendered "(Unnamed)". Using the empty string simplifies autocompletions, which will actually have to insert nothing at all (in addition to terminating the string). * Since unnamed buffers are now special in the popup list, we can render them with special icons as well. Currently, only on Curses we use a file symbol with a question mark. There doesn't appear to be a fitting standard Freedesktop icon to use on GTK and there isn't even any fitting standard emblem to lay over the default file icon.
Diffstat (limited to 'src')
-rw-r--r--src/interface-curses/curses-icons.c4
-rw-r--r--src/interface-curses/curses-info-popup.c20
-rw-r--r--src/interface-curses/curses-utils.h3
-rw-r--r--src/interface-curses/interface.c38
-rw-r--r--src/interface-gtk/gtk-info-popup.c15
-rw-r--r--src/interface-gtk/gtk-label.c28
-rw-r--r--src/interface-gtk/interface.c18
-rw-r--r--src/interface.h12
-rw-r--r--src/ring.c9
-rw-r--r--src/string-utils.h2
10 files changed, 93 insertions, 56 deletions
diff --git a/src/interface-curses/curses-icons.c b/src/interface-curses/curses-icons.c
index 7c021d6..d932104 100644
--- a/src/interface-curses/curses-icons.c
+++ b/src/interface-curses/curses-icons.c
@@ -362,6 +362,10 @@ teco_curses_icon_cmp(const void *a, const void *b)
gunichar
teco_curses_icons_lookup_file(const gchar *filename)
{
+ if (!filename || !*filename)
+ /* "(Unnamed)" file */
+ return 0xf1036; /* 󱀶 */
+
g_autofree gchar *basename = g_path_get_basename(filename);
const teco_curses_icon_t *icon;
diff --git a/src/interface-curses/curses-info-popup.c b/src/interface-curses/curses-info-popup.c
index c51a99b..8da6fbb 100644
--- a/src/interface-curses/curses-info-popup.c
+++ b/src/interface-curses/curses-info-popup.c
@@ -19,6 +19,8 @@
#include "config.h"
#endif
+#include <string.h>
+
#include <glib.h>
#include <curses.h>
@@ -38,6 +40,7 @@ typedef struct {
teco_stailq_entry_t entry;
teco_popup_entry_type_t type;
+ /** entry name or empty string for the "(Unnamed)" buffer */
teco_string_t name;
gboolean highlight;
} teco_popup_entry_t;
@@ -122,25 +125,32 @@ teco_curses_info_popup_init_pad(teco_curses_info_popup_t *ctx, attr_t attr)
if (entry->highlight)
wattron(ctx->pad, A_BOLD);
+ teco_string_t name = entry->name;
+ if (!name.len) {
+ name.data = TECO_UNNAMED_FILE;
+ name.len = strlen(name.data);
+ }
+
switch (entry->type) {
case TECO_POPUP_FILE:
- g_assert(!teco_string_contains(&entry->name, '\0'));
+ g_assert(!teco_string_contains(&name, '\0'));
if (teco_ed & TECO_ED_ICONS) {
+ /* "(Unnamed)" buffer is looked up as "" */
teco_curses_add_wc(ctx->pad, teco_curses_icons_lookup_file(entry->name.data));
waddch(ctx->pad, ' ');
}
- teco_curses_format_filename(ctx->pad, entry->name.data, -1);
+ teco_curses_format_filename(ctx->pad, name.data, -1);
break;
case TECO_POPUP_DIRECTORY:
- g_assert(!teco_string_contains(&entry->name, '\0'));
+ g_assert(!teco_string_contains(&name, '\0'));
if (teco_ed & TECO_ED_ICONS) {
teco_curses_add_wc(ctx->pad, teco_curses_icons_lookup_dir(entry->name.data));
waddch(ctx->pad, ' ');
}
- teco_curses_format_filename(ctx->pad, entry->name.data, -1);
+ teco_curses_format_filename(ctx->pad, name.data, -1);
break;
default:
- teco_curses_format_str(ctx->pad, entry->name.data, entry->name.len, -1);
+ teco_curses_format_str(ctx->pad, name.data, name.len, -1);
break;
}
diff --git a/src/interface-curses/curses-utils.h b/src/interface-curses/curses-utils.h
index 18cdd3d..c63c747 100644
--- a/src/interface-curses/curses-utils.h
+++ b/src/interface-curses/curses-utils.h
@@ -20,6 +20,9 @@
#include <curses.h>
+/** what is displayed for unnamed buffers in the info line and popups */
+#define TECO_UNNAMED_FILE "(Unnamed)"
+
guint teco_curses_format_str(WINDOW *win, const gchar *str, gsize len, gint max_width);
guint teco_curses_format_filename(WINDOW *win, const gchar *filename, gint max_width);
diff --git a/src/interface-curses/interface.c b/src/interface-curses/interface.c
index 073ff86..833b564 100644
--- a/src/interface-curses/interface.c
+++ b/src/interface-curses/interface.c
@@ -138,8 +138,6 @@ static gint teco_xterm_version(void) G_GNUC_UNUSED;
static gint teco_interface_blocking_getch(void);
-#define UNNAMED_FILE "(Unnamed)"
-
/**
* Get bright variant of one of the 8 standard
* curses colors.
@@ -202,6 +200,7 @@ static struct {
TECO_INFO_TYPE_BUFFER = 0,
TECO_INFO_TYPE_QREG
} info_type;
+ /* current document's name or empty string for "(Unnamed)" buffer */
teco_string_t info_current;
gboolean info_dirty;
@@ -437,12 +436,6 @@ teco_interface_init(void)
teco_curses_info_popup_init(&teco_interface.popup);
- /*
- * Make sure we have a string for the info line
- * even if teco_interface_info_update() is never called.
- */
- teco_string_init(&teco_interface.info_current, PACKAGE_NAME, strlen(PACKAGE_NAME));
-
teco_cmdline_init();
/*
* The default INDIC_STRIKE wouldn't be visible.
@@ -1160,26 +1153,30 @@ teco_interface_draw_info(void)
waddstr(teco_interface.info_window, PACKAGE_NAME " ");
+ teco_string_t info_current = teco_interface.info_current;
+ if (!info_current.len) {
+ info_current.data = TECO_UNNAMED_FILE;
+ info_current.len = strlen(info_current.data);
+ }
+
switch (teco_interface.info_type) {
case TECO_INFO_TYPE_QREG:
info_type_str = PACKAGE_NAME " - <QRegister> ";
teco_curses_add_wc(teco_interface.info_window,
teco_ed & TECO_ED_ICONS ? TECO_CURSES_ICONS_QREG : '-');
waddstr(teco_interface.info_window, " <QRegister> ");
- /* same formatting as in command lines */
teco_curses_format_str(teco_interface.info_window,
- teco_interface.info_current.data,
- teco_interface.info_current.len, -1);
+ info_current.data, info_current.len, -1);
break;
case TECO_INFO_TYPE_BUFFER:
info_type_str = PACKAGE_NAME " - <Buffer> ";
- g_assert(!teco_string_contains(&teco_interface.info_current, '\0'));
+ g_assert(!teco_string_contains(&info_current, '\0'));
+ /* "(Unnamed)" buffer has to be looked up as "" */
teco_curses_add_wc(teco_interface.info_window,
teco_ed & TECO_ED_ICONS ? teco_curses_icons_lookup_file(teco_interface.info_current.data) : '-');
waddstr(teco_interface.info_window, " <Buffer> ");
- teco_curses_format_filename(teco_interface.info_window,
- teco_interface.info_current.data,
+ teco_curses_format_filename(teco_interface.info_window, info_current.data,
getmaxx(teco_interface.info_window) -
getcurx(teco_interface.info_window) - 1);
waddch(teco_interface.info_window, teco_interface.info_dirty ? '*' : ' ');
@@ -1195,8 +1192,7 @@ teco_interface_draw_info(void)
* Make sure the title will consist only of printable characters.
*/
g_autofree gchar *info_current_printable;
- info_current_printable = teco_string_echo(teco_interface.info_current.data,
- teco_interface.info_current.len);
+ info_current_printable = teco_string_echo(info_current.data, info_current.len);
g_autofree gchar *title = g_strconcat(info_type_str, info_current_printable,
teco_interface.info_dirty ? "*" : "", NULL);
teco_interface_set_window_title(title);
@@ -1216,10 +1212,9 @@ teco_interface_info_update_qreg(const teco_qreg_t *reg)
void
teco_interface_info_update_buffer(const teco_buffer_t *buffer)
{
- const gchar *filename = buffer->filename ? : UNNAMED_FILE;
-
teco_string_clear(&teco_interface.info_current);
- teco_string_init(&teco_interface.info_current, filename, strlen(filename));
+ teco_string_init(&teco_interface.info_current, buffer->filename,
+ buffer->filename ? strlen(buffer->filename) : 0);
teco_interface.info_dirty = buffer->state > TECO_BUFFER_CLEAN;
teco_interface.info_type = TECO_INFO_TYPE_BUFFER;
/* NOTE: drawn in teco_interface_event_loop_iter() */
@@ -1865,7 +1860,10 @@ teco_interface_process_mevent(MEVENT *event, GError **error)
event->y, event->x);
if (insert && machine->current->insert_completion_cb) {
- /* successfully clicked popup item */
+ /*
+ * Successfully clicked popup item.
+ * `insert` is the empty string for the "(Unnamed)" buffer.
+ */
const teco_string_t insert_suffix = {insert->data + teco_interface.popup_prefix_len,
insert->len - teco_interface.popup_prefix_len};
if (!machine->current->insert_completion_cb(machine, &insert_suffix, error))
diff --git a/src/interface-gtk/gtk-info-popup.c b/src/interface-gtk/gtk-info-popup.c
index aaa0a65..3ddd10a 100644
--- a/src/interface-gtk/gtk-info-popup.c
+++ b/src/interface-gtk/gtk-info-popup.c
@@ -37,6 +37,7 @@ typedef struct {
teco_stailq_entry_t entry;
teco_popup_entry_type_t type;
+ /** entry name or empty string for the "(Unnamed)" buffer */
teco_string_t name;
gboolean highlight;
} teco_popup_entry_t;
@@ -249,6 +250,10 @@ teco_gtk_info_popup_new(void)
GIcon *
teco_gtk_info_popup_get_icon_for_path(const gchar *path, const gchar *fallback_name)
{
+ if (!path || !*path)
+ /* "(Unnamed)" file */
+ return g_icon_new_for_string(fallback_name, NULL);
+
GIcon *icon = NULL;
g_autoptr(GFile) file = g_file_new_for_path(path);
@@ -299,7 +304,7 @@ teco_gtk_info_popup_add(TecoGtkInfoPopup *self, teco_popup_entry_type_t type,
static void
teco_gtk_info_popup_idle_add(TecoGtkInfoPopup *self, teco_popup_entry_type_t type,
- const gchar *name, gssize len, gboolean highlight)
+ const gchar *name, gsize len, gboolean highlight)
{
g_return_if_fail(self != NULL);
g_return_if_fail(TECO_IS_GTK_INFO_POPUP(self));
@@ -318,12 +323,8 @@ teco_gtk_info_popup_idle_add(TecoGtkInfoPopup *self, teco_popup_entry_type_t typ
const gchar *fallback = type == TECO_POPUP_FILE ? "text-x-generic"
: "folder";
- /*
- * `name` is not guaranteed to be null-terminated.
- */
- g_autofree gchar *path = len < 0 ? g_strdup(name) : g_strndup(name, len);
-
- g_autoptr(GIcon) icon = teco_gtk_info_popup_get_icon_for_path(path, fallback);
+ /* name comes from a teco_string_t and is guaranteed to be null-terminated */
+ g_autoptr(GIcon) icon = teco_gtk_info_popup_get_icon_for_path(name, fallback);
if (icon) {
gint width, height;
gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height);
diff --git a/src/interface-gtk/gtk-label.c b/src/interface-gtk/gtk-label.c
index e4b2823..e148652 100644
--- a/src/interface-gtk/gtk-label.c
+++ b/src/interface-gtk/gtk-label.c
@@ -32,7 +32,9 @@
#include "gtk-label.h"
-#define GDK_TO_PANGO_COLOR(X) ((guint16)((X) * G_MAXUINT16))
+#define TECO_UNNAMED_FILE "(Unnamed)"
+
+#define GDK_TO_PANGO_COLOR(X) ((guint16)((X) * G_MAXUINT16))
struct _TecoGtkLabel {
GtkLabel parent_instance;
@@ -40,6 +42,7 @@ struct _TecoGtkLabel {
PangoColor fg, bg;
guint16 fg_alpha, bg_alpha;
+ /** text backing the label or empty string for "(Unnamed)" buffer */
teco_string_t string;
};
@@ -251,19 +254,22 @@ teco_gtk_label_set_text(TecoGtkLabel *self, const gchar *str, gssize len)
teco_string_clear(&self->string);
teco_string_init(&self->string, str, len < 0 ? strlen(str) : len);
+ teco_string_t string = self->string;
+ if (!string.len) {
+ string.data = TECO_UNNAMED_FILE;
+ string.len = strlen(string.data);
+ }
+
g_autofree gchar *plaintext = NULL;
+ PangoAttrList *attribs = NULL;
- if (self->string.len > 0) {
- PangoAttrList *attribs = NULL;
+ teco_gtk_label_parse_string(string.data, string.len,
+ &self->fg, self->fg_alpha,
+ &self->bg, self->bg_alpha,
+ &attribs, &plaintext);
- teco_gtk_label_parse_string(self->string.data, self->string.len,
- &self->fg, self->fg_alpha,
- &self->bg, self->bg_alpha,
- &attribs, &plaintext);
-
- gtk_label_set_attributes(GTK_LABEL(self), attribs);
- pango_attr_list_unref(attribs);
- }
+ gtk_label_set_attributes(GTK_LABEL(self), attribs);
+ pango_attr_list_unref(attribs);
gtk_label_set_text(GTK_LABEL(self), plaintext);
}
diff --git a/src/interface-gtk/interface.c b/src/interface-gtk/interface.c
index d7fc4c4..2b292a3 100644
--- a/src/interface-gtk/interface.c
+++ b/src/interface-gtk/interface.c
@@ -78,7 +78,7 @@ static gboolean teco_interface_window_delete_cb(GtkWidget *widget, GdkEventAny *
static gboolean teco_interface_sigterm_handler(gpointer user_data) G_GNUC_UNUSED;
static gchar teco_interface_get_ansi_key(GdkEventKey *event);
-#define UNNAMED_FILE "(Unnamed)"
+#define TECO_UNNAMED_FILE "(Unnamed)"
#define USER_CSS_FILE ".teco_css"
@@ -102,6 +102,7 @@ static struct {
TECO_INFO_TYPE_BUFFER_DIRTY,
TECO_INFO_TYPE_QREG
} info_type;
+ /* current document's name or empty string for "(Unnamed)" buffer */
teco_string_t info_current;
gboolean no_csd;
@@ -501,8 +502,13 @@ teco_interface_refresh_info(void)
gtk_style_context_remove_class(style, "dirty");
g_auto(teco_string_t) info_current_temp;
- teco_string_init(&info_current_temp,
- teco_interface.info_current.data, teco_interface.info_current.len);
+
+ if (!teco_interface.info_current.len)
+ teco_string_init(&info_current_temp, TECO_UNNAMED_FILE, strlen(TECO_UNNAMED_FILE));
+ else
+ teco_string_init(&info_current_temp,
+ teco_interface.info_current.data, teco_interface.info_current.len);
+
if (teco_interface.info_type == TECO_INFO_TYPE_BUFFER_DIRTY)
teco_string_append_c(&info_current_temp, '*');
teco_gtk_label_set_text(TECO_GTK_LABEL(teco_interface.info_name_widget),
@@ -570,10 +576,9 @@ teco_interface_info_update_qreg(const teco_qreg_t *reg)
void
teco_interface_info_update_buffer(const teco_buffer_t *buffer)
{
- const gchar *filename = buffer->filename ? : UNNAMED_FILE;
-
teco_string_clear(&teco_interface.info_current);
- teco_string_init(&teco_interface.info_current, filename, strlen(filename));
+ teco_string_init(&teco_interface.info_current, buffer->filename,
+ buffer->filename ? strlen(buffer->filename) : 0);
teco_interface.info_type = buffer->state > TECO_BUFFER_CLEAN
? TECO_INFO_TYPE_BUFFER_DIRTY : TECO_INFO_TYPE_BUFFER;
}
@@ -1472,6 +1477,7 @@ static void
teco_interface_popup_clicked_cb(GtkWidget *popup, gchar *str, gulong len, gpointer user_data)
{
g_assert(len >= teco_interface.popup_prefix_len);
+ /* str is an empty string for the "(Unnamed)" buffer */
const teco_string_t insert = {str+teco_interface.popup_prefix_len, len-teco_interface.popup_prefix_len};
teco_machine_t *machine = &teco_cmdline.machine.parent;
diff --git a/src/interface.h b/src/interface.h
index 8ab66ef..24df14c 100644
--- a/src/interface.h
+++ b/src/interface.h
@@ -135,7 +135,17 @@ typedef enum {
TECO_POPUP_DIRECTORY
} teco_popup_entry_type_t;
-/** @pure */
+/**
+ * Add entry to popup.
+ *
+ * @param type Entry type
+ * @param name
+ * Name string of the entry or NULL for "(Unnamed)".
+ * It is not necessarily null-terminated.
+ * @param name_len Length of string in name
+ * @param highlight Whether to highlight the entry
+ * @pure
+ */
void teco_interface_popup_add(teco_popup_entry_type_t type,
const gchar *name, gsize name_len, gboolean highlight);
/** @pure */
diff --git a/src/ring.c b/src/ring.c
index bc04e50..22db7ce 100644
--- a/src/ring.c
+++ b/src/ring.c
@@ -517,11 +517,10 @@ teco_state_edit_file_initial(teco_machine_main_t *ctx, GError **error)
ctx->flags.allow_filename = TRUE;
if (id == 0) {
- for (teco_buffer_t *cur = teco_ring_first(); cur; cur = teco_buffer_next(cur)) {
- const gchar *filename = cur->filename ? : "(Unnamed)";
- teco_interface_popup_add(TECO_POPUP_FILE, filename,
- strlen(filename), cur == teco_ring_current);
- }
+ for (teco_buffer_t *cur = teco_ring_first(); cur; cur = teco_buffer_next(cur))
+ teco_interface_popup_add(TECO_POPUP_FILE, cur->filename,
+ cur->filename ? strlen(cur->filename) : 0,
+ cur == teco_ring_current);
teco_interface_popup_show(0);
} else if (id > 0) {
diff --git a/src/string-utils.h b/src/string-utils.h
index 2491d07..af2c8a1 100644
--- a/src/string-utils.h
+++ b/src/string-utils.h
@@ -102,7 +102,7 @@ teco_string_init(teco_string_t *target, const gchar *str, gsize len)
static inline void
teco_string_init_chunk(teco_string_t *target, const gchar *str, gssize len, GStringChunk *chunk)
{
- target->data = g_string_chunk_insert_len(chunk, str, len);
+ target->data = g_string_chunk_insert_len(chunk, str ? : "", len);
target->len = len;
}