7 Commits
Author SHA1 Message Date
NotAlexNoyleandClaude Opus 4.8 e3b229c464 Add MIT License
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 14:17:33 -04:00
rabfulton effa685762 Merge pull request #1 from FenrirTheGray/fix/reload-file-crash
Fix error when reloading file more than once
2026-04-10 11:28:26 +01:00
Aleksandar Colovic a30ce043f4 Fix crash when reloading file more than once
markyd_app_open_file() invalidates the current path string, so calling
it with the pointer from markyd_app_get_current_path() works the first
time but causes a use-after-free on subsequent reloads. Copy the path
with g_strdup() before passing it to open_file, and free it afterwards.
2026-04-08 18:46:24 +02:00
Robert Fulton 3d36df9847 Update README.md 2026-02-26 02:09:07 +00:00
Robert Fulton 0cce704eec Support hanging indents for list items 2026-02-26 02:06:58 +00:00
Robert Fulton 55eff8c494 Fix for search within tables 2026-02-26 01:50:11 +00:00
Robert Fulton c40a410d7d Add support for local images 2026-02-26 01:18:04 +00:00
6 changed files with 440 additions and 31 deletions

No files matched your search

+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Robert Fulton
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+1
View File
@@ -12,6 +12,7 @@ A lightweight GTK 3 markdown viewer for desktop Linux. It is ideal as your defau
- **Lightweight** - Pure C, no web technologies, fast startup
- **Hyperlink support** - Left click opens links and internal anchors
- **Document search** - `Ctrl+F` with next/previous match navigation
- **Local image support** - Local images are resized to fit the the document window
## Supported Markdown
+200
View File
@@ -1,4 +1,5 @@
#include "editor.h"
#include "app.h"
#include "markdown.h"
#include <string.h>
@@ -10,7 +11,13 @@ static gboolean on_leave_notify(GtkWidget *widget, GdkEventCrossing *event,
gpointer user_data);
static void apply_markdown(MarkydEditor *self);
static void schedule_markdown_apply(MarkydEditor *self);
static void render_image_widgets(MarkydEditor *self);
static void render_table_widgets(MarkydEditor *self);
static void refresh_image_widget_scales(MarkydEditor *self);
static gboolean resolve_image_source_path(MarkydEditor *self, const gchar *src,
gchar **out_path);
static void on_text_view_size_allocate(GtkWidget *widget, GtkAllocation *allocation,
gpointer user_data);
static gchar *get_url_from_iter_tags(GtkTextIter *iter) {
GSList *tags;
@@ -133,10 +140,193 @@ static void apply_markdown(MarkydEditor *self) {
self->updating_tags = TRUE;
markdown_apply_tags(self->buffer,
self->source_content ? self->source_content : "");
render_image_widgets(self);
render_table_widgets(self);
refresh_image_widget_scales(self);
self->updating_tags = FALSE;
}
static gboolean resolve_image_source_path(MarkydEditor *self, const gchar *src,
gchar **out_path) {
gchar *path = NULL;
if (out_path) {
*out_path = NULL;
}
if (!self || !src || src[0] == '\0' || !out_path) {
return FALSE;
}
if (g_uri_parse_scheme(src) != NULL) {
if (g_str_has_prefix(src, "file://")) {
path = g_filename_from_uri(src, NULL, NULL);
} else {
return FALSE;
}
} else if (g_path_is_absolute(src)) {
path = g_strdup(src);
} else {
const gchar *current_path = markyd_app_get_current_path(self->app);
if (current_path && current_path[0] != '\0') {
gchar *dir = g_path_get_dirname(current_path);
path = g_build_filename(dir, src, NULL);
g_free(dir);
} else {
path = g_strdup(src);
}
}
if (!path) {
return FALSE;
}
if (!g_file_test(path, G_FILE_TEST_EXISTS)) {
g_free(path);
return FALSE;
}
*out_path = path;
return TRUE;
}
static gint get_image_max_width(MarkydEditor *self) {
GtkAllocation alloc;
gint width;
if (!self || !self->text_view) {
return 0;
}
gtk_widget_get_allocation(self->text_view, &alloc);
width = alloc.width - gtk_text_view_get_left_margin(GTK_TEXT_VIEW(self->text_view)) -
gtk_text_view_get_right_margin(GTK_TEXT_VIEW(self->text_view)) - 24;
return MAX(width, 64);
}
static void scale_image_widget(GtkWidget *widget, gint max_width) {
GtkWidget *image;
GdkPixbuf *orig;
gint ow;
gint oh;
if (!widget || max_width <= 0) {
return;
}
image = g_object_get_data(G_OBJECT(widget), "viewmd-image-widget-child");
orig = g_object_get_data(G_OBJECT(widget), "viewmd-image-orig-pixbuf");
if (!GTK_IS_IMAGE(image) || !GDK_IS_PIXBUF(orig)) {
return;
}
ow = gdk_pixbuf_get_width(orig);
oh = gdk_pixbuf_get_height(orig);
if (ow <= 0 || oh <= 0) {
return;
}
if (ow > max_width) {
gint nh = (gint)(((gdouble)oh * (gdouble)max_width) / (gdouble)ow);
GdkPixbuf *scaled =
gdk_pixbuf_scale_simple(orig, max_width, MAX(nh, 1), GDK_INTERP_BILINEAR);
gtk_image_set_from_pixbuf(GTK_IMAGE(image), scaled);
if (scaled) {
g_object_unref(scaled);
}
} else {
gtk_image_set_from_pixbuf(GTK_IMAGE(image), orig);
}
}
static void refresh_image_widget_scales(MarkydEditor *self) {
GtkTextIter iter;
GtkTextIter end;
gint max_width;
if (!self || !self->buffer) {
return;
}
max_width = get_image_max_width(self);
gtk_text_buffer_get_bounds(self->buffer, &iter, &end);
while (!gtk_text_iter_equal(&iter, &end)) {
GtkTextChildAnchor *anchor = gtk_text_iter_get_child_anchor(&iter);
if (anchor &&
g_object_get_data(G_OBJECT(anchor), VIEWMD_IMAGE_ANCHOR_DATA) != NULL) {
GtkWidget *image_widget =
g_object_get_data(G_OBJECT(anchor), VIEWMD_IMAGE_WIDGET_DATA);
if (image_widget) {
scale_image_widget(image_widget, max_width);
}
}
gtk_text_iter_forward_char(&iter);
}
}
static void render_image_widgets(MarkydEditor *self) {
GtkTextIter iter;
GtkTextIter end;
gint max_width;
if (!self || !self->buffer || !self->text_view) {
return;
}
max_width = get_image_max_width(self);
gtk_text_buffer_get_bounds(self->buffer, &iter, &end);
while (!gtk_text_iter_equal(&iter, &end)) {
GtkTextChildAnchor *anchor = gtk_text_iter_get_child_anchor(&iter);
if (anchor &&
g_object_get_data(G_OBJECT(anchor), VIEWMD_IMAGE_ANCHOR_DATA) != NULL) {
GtkWidget *image_widget =
g_object_get_data(G_OBJECT(anchor), VIEWMD_IMAGE_WIDGET_DATA);
if (!image_widget) {
const gchar *src =
g_object_get_data(G_OBJECT(anchor), VIEWMD_IMAGE_SRC_DATA);
const gchar *alt =
g_object_get_data(G_OBJECT(anchor), VIEWMD_IMAGE_ALT_DATA);
gchar *path = NULL;
if (resolve_image_source_path(self, src, &path)) {
GError *err = NULL;
GdkPixbuf *orig = gdk_pixbuf_new_from_file(path, &err);
if (orig) {
GtkWidget *event_box = gtk_event_box_new();
GtkWidget *image = gtk_image_new();
gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box), FALSE);
gtk_widget_set_halign(event_box, GTK_ALIGN_START);
gtk_container_add(GTK_CONTAINER(event_box), image);
g_object_set_data(G_OBJECT(event_box), "viewmd-image-widget-child",
image);
g_object_set_data_full(G_OBJECT(event_box), "viewmd-image-orig-pixbuf",
orig, g_object_unref);
scale_image_widget(event_box, max_width);
image_widget = event_box;
} else if (err) {
g_error_free(err);
}
}
if (!image_widget) {
GtkWidget *fallback = gtk_label_new(alt && alt[0] != '\0' ? alt : src);
gtk_widget_set_halign(fallback, GTK_ALIGN_START);
gtk_style_context_add_class(gtk_widget_get_style_context(fallback),
"dim-label");
image_widget = fallback;
}
gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(self->text_view),
image_widget, anchor);
gtk_widget_show_all(image_widget);
g_object_set_data(G_OBJECT(anchor), VIEWMD_IMAGE_WIDGET_DATA, image_widget);
g_free(path);
} else {
scale_image_widget(image_widget, max_width);
}
}
gtk_text_iter_forward_char(&iter);
}
}
static void render_table_widgets(MarkydEditor *self) {
GtkTextIter iter;
GtkTextIter end;
@@ -214,10 +404,20 @@ MarkydEditor *markyd_editor_new(MarkydApp *app) {
G_CALLBACK(on_motion_notify), self);
g_signal_connect(self->text_view, "leave-notify-event",
G_CALLBACK(on_leave_notify), self);
g_signal_connect(self->text_view, "size-allocate",
G_CALLBACK(on_text_view_size_allocate), self);
return self;
}
static void on_text_view_size_allocate(GtkWidget *widget, GtkAllocation *allocation,
gpointer user_data) {
MarkydEditor *self = (MarkydEditor *)user_data;
(void)widget;
(void)allocation;
refresh_image_widget_scales(self);
}
void markyd_editor_free(MarkydEditor *self) {
if (!self) {
return;
+154 -25
View File
@@ -29,6 +29,11 @@ typedef struct {
guint next_index;
} ListState;
typedef struct {
gint left_margin;
gint indent;
} ListLayoutSpec;
typedef struct {
MD_BLOCKTYPE type;
guint pushed_tags;
@@ -74,6 +79,9 @@ typedef struct {
ViewmdTableRow *table_current_row;
GString *table_cell_text;
guint table_current_col;
gboolean in_image;
gchar *image_src;
GString *image_alt;
GArray *code_blocks; /* CodeBlockRange */
gint current_code_start_offset;
const MarkydLanguageHighlight *current_code_language;
@@ -274,6 +282,14 @@ static void update_newline_state(RenderCtx *ctx, const gchar *text, gsize len) {
}
}
static void note_non_newline_output(RenderCtx *ctx) {
if (!ctx) {
return;
}
ctx->has_output = TRUE;
ctx->trailing_newlines = 0;
}
static void apply_tag_by_offsets(GtkTextBuffer *buffer, GtkTextTag *tag,
gint start_offset, gint end_offset) {
GtkTextIter start;
@@ -337,6 +353,61 @@ static void ensure_newlines(RenderCtx *ctx, guint min_newlines) {
}
}
static guint decimal_digits(guint value) {
guint digits = 1;
while (value >= 10) {
value /= 10;
digits++;
}
return digits;
}
static guint list_marker_columns(RenderCtx *ctx) {
ListState *list;
if (!ctx || !ctx->list_stack || ctx->list_stack->len == 0) {
return 2; /* "• " */
}
list = &g_array_index(ctx->list_stack, ListState, ctx->list_stack->len - 1);
if (list->ordered) {
return decimal_digits(MAX(1, list->next_index)) + 2; /* "12. " */
}
return 2; /* "• " */
}
static ListLayoutSpec compute_list_layout(guint depth, guint quote_depth,
guint marker_cols) {
ListLayoutSpec spec = {0, 0};
guint depth_index = (depth > 0) ? (depth - 1) : 0;
gint marker_px = (gint)CLAMP((gint)marker_cols * 9, 16, 64);
gint base_indent = 20 + ((gint)depth_index * 22) + ((gint)quote_depth * 24);
spec.left_margin = base_indent + marker_px;
spec.indent = -marker_px;
return spec;
}
static GtkTextTag *ensure_list_layout_tag(RenderCtx *ctx, guint depth,
guint quote_depth,
guint marker_cols) {
gchar *name;
GtkTextTag *tag;
ListLayoutSpec spec;
if (!ctx || !ctx->buffer) {
return NULL;
}
name = g_strdup_printf("list_layout_%u_%u_%u", depth, quote_depth, marker_cols);
tag = lookup_tag(ctx->buffer, name);
if (!tag) {
spec = compute_list_layout(depth, quote_depth, marker_cols);
tag = gtk_text_buffer_create_tag(ctx->buffer, name, "left-margin",
spec.left_margin, "indent", spec.indent, NULL);
}
g_free(name);
return tag;
}
static void push_active_tag(RenderCtx *ctx, GtkTextTag *tag, guint *counter) {
if (!ctx || !tag) {
return;
@@ -621,12 +692,19 @@ static void table_search_index_free(gpointer data) {
static void table_emit_hidden_search_text(RenderCtx *ctx, ViewmdTable *table,
GtkTextChildAnchor *anchor) {
ViewmdTableSearchIndex *index;
gboolean saved_has_output;
guint saved_trailing_newlines;
const gchar *cell_sep = " ";
if (!ctx || !ctx->buffer || !table || !anchor || !table->rows ||
table->rows->len == 0 || table->col_count == 0) {
return;
}
/* Hidden search text must not affect visible block spacing state. */
saved_has_output = ctx->has_output;
saved_trailing_newlines = ctx->trailing_newlines;
index = g_new0(ViewmdTableSearchIndex, 1);
index->cells = g_array_new(FALSE, FALSE, sizeof(ViewmdTableSearchCellRange));
index->start_offset = gtk_text_iter_get_offset(&ctx->iter);
@@ -665,14 +743,10 @@ static void table_emit_hidden_search_text(RenderCtx *ctx, ViewmdTable *table,
g_free(plain);
if (c + 1 < table->col_count) {
gtk_text_buffer_insert(ctx->buffer, &ctx->iter, "\t", 1);
if (!(r + 1 == table->rows->len && c + 1 == table->col_count)) {
gtk_text_buffer_insert(ctx->buffer, &ctx->iter, cell_sep, -1);
}
}
if (r + 1 < table->rows->len) {
gtk_text_buffer_insert(ctx->buffer, &ctx->iter, "\n", 1);
}
}
index->end_offset = gtk_text_iter_get_offset(&ctx->iter);
@@ -684,6 +758,9 @@ static void table_emit_hidden_search_text(RenderCtx *ctx, ViewmdTable *table,
} else {
table_search_index_free(index);
}
ctx->has_output = saved_has_output;
ctx->trailing_newlines = saved_trailing_newlines;
}
static void table_capture_span_enter(RenderCtx *ctx, MD_SPANTYPE type) {
@@ -804,6 +881,7 @@ static void table_emit_anchor(RenderCtx *ctx) {
}
anchor = gtk_text_buffer_create_child_anchor(ctx->buffer, &ctx->iter);
note_non_newline_output(ctx);
g_object_set_data(G_OBJECT(anchor), VIEWMD_TABLE_ANCHOR_DATA, GINT_TO_POINTER(1));
g_object_set_data_full(G_OBJECT(anchor), TABLE_MODEL_DATA_KEY, ctx->table_model,
viewmd_table_free);
@@ -811,31 +889,45 @@ static void table_emit_anchor(RenderCtx *ctx) {
/* Keep table text searchable via Ctrl+F without showing duplicate content. */
table_emit_hidden_search_text(ctx, ctx->table_model, anchor);
/* Force at least one hard line break after the embedded table widget so
* following markdown never shares the same visual line. */
insert_cstr(ctx, "\n");
ctx->table_model = NULL;
}
static void image_emit_anchor(RenderCtx *ctx) {
GtkTextChildAnchor *anchor;
gchar *alt = NULL;
if (!ctx || !ctx->buffer || !ctx->image_src || ctx->image_src[0] == '\0') {
return;
}
anchor = gtk_text_buffer_create_child_anchor(ctx->buffer, &ctx->iter);
note_non_newline_output(ctx);
g_object_set_data(G_OBJECT(anchor), VIEWMD_IMAGE_ANCHOR_DATA, GINT_TO_POINTER(1));
g_object_set_data_full(G_OBJECT(anchor), VIEWMD_IMAGE_SRC_DATA,
g_strdup(ctx->image_src), g_free);
if (ctx->image_alt && ctx->image_alt->len > 0) {
alt = g_strdup(ctx->image_alt->str);
} else {
alt = g_strdup("");
}
g_object_set_data_full(G_OBJECT(anchor), VIEWMD_IMAGE_ALT_DATA, alt, g_free);
/* Keep following markdown on a new visual line after embedded images. */
insert_cstr(ctx, "\n");
}
static void insert_list_marker(RenderCtx *ctx) {
ListState *list;
gchar *ordered = NULL;
gint marker_start_offset;
gint marker_end_offset;
guint depth_indent;
if (!ctx || ctx->list_stack->len == 0) {
return;
}
list = &g_array_index(ctx->list_stack, ListState, ctx->list_stack->len - 1);
depth_indent = ((ctx->list_stack->len > 0) ? (ctx->list_stack->len - 1) * 2 : 0) +
(ctx->quote_depth * 2);
for (guint i = 0; i < depth_indent; i++) {
insert_cstr(ctx, " ");
}
marker_start_offset = gtk_text_iter_get_offset(&ctx->iter);
if (list->ordered) {
@@ -925,13 +1017,19 @@ static int on_enter_block(MD_BLOCKTYPE type, void *detail, void *userdata) {
}
case MD_BLOCK_LI:
ensure_newlines(ctx, 1);
push_active_tag_by_name(ctx, TAG_LIST,
&g_array_index(ctx->block_stack, BlockState,
ctx->block_stack->len - 1)
.pushed_tags);
insert_list_marker(ctx);
ctx->list_item_prefix_pending = TRUE;
{
guint marker_cols = list_marker_columns(ctx);
GtkTextTag *layout_tag = ensure_list_layout_tag(
ctx, ctx->list_stack->len, ctx->quote_depth, marker_cols);
ensure_newlines(ctx, 1);
push_active_tag(ctx, layout_tag,
&g_array_index(ctx->block_stack, BlockState,
ctx->block_stack->len - 1)
.pushed_tags);
insert_list_marker(ctx);
ctx->list_item_prefix_pending = TRUE;
}
break;
case MD_BLOCK_HR: {
@@ -1105,6 +1203,19 @@ static int on_enter_span(MD_SPANTYPE type, void *detail, void *userdata) {
break;
}
case MD_SPAN_IMG: {
MD_SPAN_IMG_DETAIL *img = (MD_SPAN_IMG_DETAIL *)detail;
g_free(ctx->image_src);
ctx->image_src = attr_to_string(img ? &img->src : NULL);
if (!ctx->image_alt) {
ctx->image_alt = g_string_new(NULL);
} else {
g_string_set_size(ctx->image_alt, 0);
}
ctx->in_image = TRUE;
break;
}
default:
break;
}
@@ -1119,6 +1230,15 @@ static int on_leave_span(MD_SPANTYPE type, void *detail, void *userdata) {
if (ctx->span_stack->len > 0) {
SpanState state =
g_array_index(ctx->span_stack, SpanState, ctx->span_stack->len - 1);
if (state.type == MD_SPAN_IMG && ctx->in_image) {
image_emit_anchor(ctx);
ctx->in_image = FALSE;
g_free(ctx->image_src);
ctx->image_src = NULL;
if (ctx->image_alt) {
g_string_set_size(ctx->image_alt, 0);
}
}
if (ctx->table_cell_text) {
table_capture_span_leave(ctx, state.type);
}
@@ -1145,7 +1265,12 @@ static int on_text(MD_TEXTTYPE type, const MD_CHAR *text, MD_SIZE size,
if (ctx->list_item_prefix_pending && rendered[0] != '\0') {
ctx->list_item_prefix_pending = FALSE;
}
if (ctx->table_cell_text) {
if (ctx->in_image) {
if (!ctx->image_alt) {
ctx->image_alt = g_string_new(NULL);
}
g_string_append(ctx->image_alt, rendered);
} else if (ctx->table_cell_text) {
table_capture_append(ctx, rendered);
} else {
insert_cstr(ctx, rendered);
@@ -1426,6 +1551,10 @@ void markdown_apply_tags(GtkTextBuffer *buffer, const gchar *source) {
if (ctx.table_cell_text) {
g_string_free(ctx.table_cell_text, TRUE);
}
g_free(ctx.image_src);
if (ctx.image_alt) {
g_string_free(ctx.image_alt, TRUE);
}
if (ctx.table_model) {
viewmd_table_free(ctx.table_model);
}
+6
View File
@@ -31,6 +31,12 @@ void markdown_update_accent_tags(GtkTextBuffer *buffer);
#define VIEWMD_TABLE_CELL_MATCH_CLASS "viewmd-table-cell-match"
#define VIEWMD_TABLE_CELL_CURRENT_CLASS "viewmd-table-cell-current"
/* GObject data keys for image anchors and metadata. */
#define VIEWMD_IMAGE_ANCHOR_DATA "viewmd-image-anchor"
#define VIEWMD_IMAGE_SRC_DATA "viewmd-image-src"
#define VIEWMD_IMAGE_ALT_DATA "viewmd-image-alt"
#define VIEWMD_IMAGE_WIDGET_DATA "viewmd-image-widget"
typedef struct {
gint row;
gint col;
+58 -6
View File
@@ -176,6 +176,8 @@ static void set_table_cell_highlight(GtkWidget *cell, gboolean match,
static gboolean scroll_to_table_cell(MarkydWindow *self, GtkWidget *cell) {
gint x = 0;
gint y = 0;
gdouble doc_x;
gdouble doc_y;
GtkAllocation alloc;
GtkAdjustment *hadj;
GtkAdjustment *vadj;
@@ -192,12 +194,21 @@ static gboolean scroll_to_table_cell(MarkydWindow *self, GtkWidget *cell) {
gtk_widget_get_allocation(cell, &alloc);
hadj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(self->scroll));
vadj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(self->scroll));
doc_x = x + (hadj ? gtk_adjustment_get_value(hadj) : 0.0);
doc_y = y + (vadj ? gtk_adjustment_get_value(vadj) : 0.0);
if (hadj) {
gtk_adjustment_clamp_page(hadj, MAX(0, x - 12), MAX(0, x + alloc.width + 12));
gdouble target = MAX(0.0, doc_x - 16.0);
gdouble max_val =
gtk_adjustment_get_upper(hadj) - gtk_adjustment_get_page_size(hadj);
gtk_adjustment_set_value(hadj, CLAMP(target, 0.0, MAX(0.0, max_val)));
}
if (vadj) {
gtk_adjustment_clamp_page(vadj, MAX(0, y - 12), MAX(0, y + alloc.height + 12));
gdouble page = gtk_adjustment_get_page_size(vadj);
gdouble target = MAX(0.0, doc_y - (page * 0.25));
gdouble max_val =
gtk_adjustment_get_upper(vadj) - gtk_adjustment_get_page_size(vadj);
gtk_adjustment_set_value(vadj, CLAMP(target, 0.0, MAX(0.0, max_val)));
}
return TRUE;
@@ -318,10 +329,11 @@ static gboolean resolve_table_match_location(MarkydWindow *self, gint start_offs
}
if (index->cells) {
ViewmdTableSearchCellRange *overlap_cell = NULL;
for (guint i = 0; i < index->cells->len; i++) {
ViewmdTableSearchCellRange *cell =
&g_array_index(index->cells, ViewmdTableSearchCellRange, i);
if (start_offset < cell->end_offset && end_offset > cell->start_offset) {
if (start_offset >= cell->start_offset && start_offset < cell->end_offset) {
if (out_row) {
*out_row = cell->row;
}
@@ -330,6 +342,18 @@ static gboolean resolve_table_match_location(MarkydWindow *self, gint start_offs
}
return TRUE;
}
if (start_offset < cell->end_offset && end_offset > cell->start_offset) {
overlap_cell = cell;
}
}
if (overlap_cell) {
if (out_row) {
*out_row = overlap_cell->row;
}
if (out_col) {
*out_col = overlap_cell->col;
}
return TRUE;
}
}
@@ -941,15 +965,17 @@ static void on_open_clicked(GtkButton *button, gpointer user_data) {
static void on_refresh_clicked(GtkButton *button, gpointer user_data) {
MarkydWindow *self = (MarkydWindow *)user_data;
const gchar *path;
const gchar *current;
gchar *path;
(void)button;
path = markyd_app_get_current_path(self->app);
if (!path || path[0] == '\0') {
current = markyd_app_get_current_path(self->app);
if (!current || current[0] == '\0') {
return;
}
path = g_strdup(current);
if (!markyd_app_open_file(self->app, path)) {
GtkWidget *error_dialog = gtk_message_dialog_new(
GTK_WINDOW(self->window),
@@ -960,6 +986,7 @@ static void on_refresh_clicked(GtkButton *button, gpointer user_data) {
gtk_dialog_run(GTK_DIALOG(error_dialog));
gtk_widget_destroy(error_dialog);
}
g_free(path);
}
static void on_settings_clicked(GtkButton *button, gpointer user_data) {
@@ -1008,6 +1035,31 @@ static gboolean on_key_press_event(GtkWidget *widget, GdkEventKey *event,
return TRUE;
}
if (event->keyval == GDK_KEY_Home || event->keyval == GDK_KEY_KP_Home) {
if (self->editor && self->editor->buffer && self->editor->text_view) {
GtkTextIter start;
GtkAdjustment *hadj = NULL;
GtkAdjustment *vadj = NULL;
gtk_text_buffer_get_start_iter(self->editor->buffer, &start);
gtk_text_buffer_place_cursor(self->editor->buffer, &start);
gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(self->editor->text_view), &start,
0.0, FALSE, 0.0, 0.0);
if (self->scroll) {
hadj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(self->scroll));
vadj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(self->scroll));
}
if (hadj) {
gtk_adjustment_set_value(hadj, gtk_adjustment_get_lower(hadj));
}
if (vadj) {
gtk_adjustment_set_value(vadj, gtk_adjustment_get_lower(vadj));
}
}
return TRUE;
}
return FALSE;
}