|
@@ -8,6 +8,7 @@ namespace Publicate.Editors {
|
|
|
|
|
|
private Paned leaflet;
|
|
|
|
|
|
+ private Box toolbar;
|
|
|
private GtkCommonMark.MarkdownView markdown_view;
|
|
|
private GtkSource.View text_view;
|
|
|
private GtkSource.Buffer source_buffer;
|
|
@@ -20,8 +21,25 @@ namespace Publicate.Editors {
|
|
|
private ViewerWindow window;
|
|
|
private TabPage page;
|
|
|
|
|
|
+ private DropDown style_selector;
|
|
|
+ private ToggleButton bold_button;
|
|
|
+ private ToggleButton italic_button;
|
|
|
+ private Button insert_link;
|
|
|
+ private Button insert_image;
|
|
|
+
|
|
|
+ private const string STYLE_HEADING_1 = "Heading 1";
|
|
|
+ private const string STYLE_HEADING_2 = "Heading 2";
|
|
|
+ private const string STYLE_HEADING_3 = "Heading 3";
|
|
|
+ private const string STYLE_HEADING_4 = "Heading 4";
|
|
|
+ private const string STYLE_HEADING_5 = "Heading 5";
|
|
|
+ private const string STYLE_HEADING_6 = "Heading 6";
|
|
|
+ private const string STYLE_QUOTATION = "Quotation";
|
|
|
+ private const string STYLE_PREFORMATTED = "Preformatted";
|
|
|
+ private const string STYLE_PARAGRAPH = "Paragraph";
|
|
|
+
|
|
|
private Spelling.Checker spell_checker;
|
|
|
private Spelling.TextBufferAdapter spell_adapter;
|
|
|
+ private bool inhibit_toolbar_updates = false;
|
|
|
|
|
|
private Gee.HashMap<string, Gdk.Pixbuf> pixbuf_cache = new Gee.HashMap<string, Gdk.Pixbuf>();
|
|
|
|
|
@@ -44,8 +62,8 @@ namespace Publicate.Editors {
|
|
|
|
|
|
public MarkdownEditor(ViewerWindow win, TabView tab_view) {
|
|
|
window = win;
|
|
|
-
|
|
|
orientation = Orientation.VERTICAL;
|
|
|
+
|
|
|
source_scroller = new ScrolledWindow ();
|
|
|
markdown_scroller = new ScrolledWindow ();
|
|
|
leaflet = new Paned(Orientation.HORIZONTAL);
|
|
@@ -57,7 +75,8 @@ namespace Publicate.Editors {
|
|
|
text_view.show_line_numbers = true;
|
|
|
text_view.auto_indent = true;
|
|
|
source_buffer = (GtkSource.Buffer) text_view.buffer;
|
|
|
- source_buffer.language = language_manager.guess_language ("file.md", "text/markdown");
|
|
|
+ language_manager.append_search_path("/usr/local/share/publicate/gtk-source-view");
|
|
|
+ source_buffer.language = language_manager.get_language("ppub-markdown");
|
|
|
text_view.hexpand = true;
|
|
|
text_view.set_wrap_mode (WrapMode.WORD_CHAR);
|
|
|
text_view.top_margin = 18;
|
|
@@ -88,12 +107,45 @@ namespace Publicate.Editors {
|
|
|
source_scroller.child = text_view;
|
|
|
markdown_scroller.child = markdown_view;
|
|
|
|
|
|
- leaflet.start_child = source_scroller;
|
|
|
+ var source_box = new Box(Orientation.VERTICAL, 0);
|
|
|
+ source_box.append(source_scroller);
|
|
|
+ leaflet.start_child = source_box;
|
|
|
leaflet.end_child = markdown_scroller;
|
|
|
|
|
|
Gtk.Settings.get_default().notify["gtk-application-prefer-dark-theme"].connect(() => configure_tags());
|
|
|
configure_tags();
|
|
|
|
|
|
+ toolbar = new Box(Orientation.HORIZONTAL, 8);
|
|
|
+ toolbar.margin_start = 8;
|
|
|
+ toolbar.margin_end = 8;
|
|
|
+ toolbar.margin_top = 8;
|
|
|
+ toolbar.margin_bottom = 8;
|
|
|
+ style_selector = new DropDown.from_strings(new string[] {STYLE_HEADING_1, STYLE_HEADING_2, STYLE_HEADING_3, STYLE_HEADING_4, STYLE_HEADING_5, STYLE_HEADING_6, STYLE_QUOTATION, STYLE_PREFORMATTED, STYLE_PARAGRAPH});
|
|
|
+ toolbar.append(style_selector);
|
|
|
+ bold_button = new ToggleButton();
|
|
|
+ bold_button.child = new Image.from_icon_name("format-text-bold-symbolic");
|
|
|
+ bold_button.tooltip_text = "Bold";
|
|
|
+ bold_button.clicked.connect(() => toggle_bold());
|
|
|
+ toolbar.append(bold_button);
|
|
|
+ italic_button = new ToggleButton();
|
|
|
+ italic_button.child = new Image.from_icon_name("format-text-italic-symbolic");
|
|
|
+ italic_button.tooltip_text = "Italic";
|
|
|
+ italic_button.clicked.connect(() => toggle_italic());
|
|
|
+ toolbar.append(italic_button);
|
|
|
+ insert_link = new Button.from_icon_name("insert-link-symbolic");
|
|
|
+ insert_link.tooltip_text = "Insert link";
|
|
|
+ insert_link.hexpand = true;
|
|
|
+ insert_link.halign = Align.END;
|
|
|
+ insert_link.clicked.connect(() => insert_link_clicked());
|
|
|
+ toolbar.append(insert_link);
|
|
|
+ insert_image = new Button.from_icon_name("insert-image-symbolic");
|
|
|
+ insert_image.tooltip_text = "Insert image";
|
|
|
+ insert_image.clicked.connect(() => insert_image_clicked());
|
|
|
+ toolbar.append(insert_image);
|
|
|
+ source_box.append(toolbar);
|
|
|
+
|
|
|
+ source_buffer.cursor_moved.connect(() => update_toolbar());
|
|
|
+ source_buffer.highlight_updated.connect(() => update_toolbar());
|
|
|
append(leaflet);
|
|
|
|
|
|
page = tab_view.add_page (this, null);
|
|
@@ -118,7 +170,7 @@ namespace Publicate.Editors {
|
|
|
text_view.buffer.set_text ((string)text, text.length);
|
|
|
set_tab_name(false);
|
|
|
|
|
|
- leaflet.position = leaflet.get_width() / 2;
|
|
|
+ Timeout.add_once(100, () => leaflet.position = int.max(300, leaflet.get_width() / 2));
|
|
|
}
|
|
|
|
|
|
public void save_asset(Ppub.Builder builder) {
|
|
@@ -208,6 +260,427 @@ namespace Publicate.Editors {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ private void update_toolbar() {
|
|
|
+ if(inhibit_toolbar_updates) {
|
|
|
+ print("Toolbar update inhibited\n");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ TextIter iter_start;
|
|
|
+ TextIter iter_end;
|
|
|
+ source_buffer.get_iter_at_offset(out iter_start, source_buffer.cursor_position);
|
|
|
+
|
|
|
+ var multi_line = false;
|
|
|
+ if(source_buffer.has_selection) {
|
|
|
+ source_buffer.get_selection_bounds(out iter_start, out iter_end);
|
|
|
+ multi_line = (iter_start.get_line() != iter_end.get_line());
|
|
|
+ }
|
|
|
+
|
|
|
+ var classes = Invercargill.Convert.ate<string>(source_buffer.get_context_classes_at_iter(iter_start));
|
|
|
+
|
|
|
+ // Disable formatting changes if the selection is over many lines or the "no-format" class is applied
|
|
|
+ toolbar.sensitive = !multi_line && !classes.any(c => c == "no-format");
|
|
|
+
|
|
|
+ print(@"$(classes.to_string(s => s, " "))\n");
|
|
|
+
|
|
|
+ set_line_style_type_in_dropdown(get_line_style_from_buffer());
|
|
|
+
|
|
|
+ bold_button.active = classes.any(c => c == "b");
|
|
|
+ italic_button.active = classes.any(c => c == "i");
|
|
|
+ }
|
|
|
+
|
|
|
+ private int get_line_style_from_buffer() {
|
|
|
+ TextIter iter;
|
|
|
+ source_buffer.get_iter_at_offset(out iter, source_buffer.cursor_position);
|
|
|
+ if(iter.ends_line()) {
|
|
|
+ iter.backward_char();
|
|
|
+ }
|
|
|
+ var classes = Invercargill.Convert.ate<string>(source_buffer.get_context_classes_at_iter(iter));
|
|
|
+ var heading = classes.first_or_default(c => c.has_prefix("h") && c.length == 2);
|
|
|
+ if(heading != null) {
|
|
|
+ var level = int.parse(heading[1:2]);
|
|
|
+ return level - 1;
|
|
|
+ }
|
|
|
+ else if(classes.any(c => c == "quot")) {
|
|
|
+ return 6;
|
|
|
+ }
|
|
|
+ else if(classes.any(c => c == "pre")) {
|
|
|
+ return 7;
|
|
|
+ }
|
|
|
+ return 8;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void remove_line_style() {
|
|
|
+ TextIter iter;
|
|
|
+ source_buffer.get_iter_at_offset(out iter, source_buffer.cursor_position);
|
|
|
+ if(iter.ends_line()) {
|
|
|
+ iter.backward_char();
|
|
|
+ }
|
|
|
+ var classes = Invercargill.Convert.ate<string>(source_buffer.get_context_classes_at_iter(iter));
|
|
|
+ var line_no = iter.get_line();
|
|
|
+
|
|
|
+ var heading = classes.first_or_default(c => c.has_prefix("h") && c.length == 2);
|
|
|
+ if(heading != null) {
|
|
|
+ var level = int.parse(heading[1:2]);
|
|
|
+ source_buffer.get_iter_at_line(out iter, line_no);
|
|
|
+ iter.forward_chars(level+1);
|
|
|
+ for(int i = 0; i <= level; i++) {
|
|
|
+ source_buffer.backspace(ref iter, false, true);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if(classes.any(c => c == "quot")) {
|
|
|
+ source_buffer.get_iter_at_line(out iter, line_no);
|
|
|
+ iter.forward_chars(2);
|
|
|
+ source_buffer.backspace(ref iter, false, true);
|
|
|
+ source_buffer.backspace(ref iter, false, true);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void set_line_style_type_in_dropdown(uint type) {
|
|
|
+ style_selector.notify["selected"].disconnect(set_line_style);
|
|
|
+ style_selector.selected = type;
|
|
|
+ style_selector.notify["selected"].connect(set_line_style);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void set_line_style() {
|
|
|
+ var style = style_selector.selected;
|
|
|
+ inhibit_toolbar_updates = true;
|
|
|
+ source_buffer.begin_user_action();
|
|
|
+ remove_line_style();
|
|
|
+
|
|
|
+ print(@"Set style $style\n");
|
|
|
+ if(style < 6) {
|
|
|
+ // Headings
|
|
|
+ format_line_heading(style + 1);
|
|
|
+ }
|
|
|
+ else if(style == 6) {
|
|
|
+ format_line_quote();
|
|
|
+ }
|
|
|
+ else if(style == 7) {
|
|
|
+ format_line_preformatted();
|
|
|
+ }
|
|
|
+
|
|
|
+ set_line_style_type_in_dropdown(style);
|
|
|
+ inhibit_toolbar_updates = false;
|
|
|
+ source_buffer.end_user_action();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void format_line_heading(uint level) {
|
|
|
+ TextIter iter;
|
|
|
+ source_buffer.get_iter_at_offset(out iter, source_buffer.cursor_position);
|
|
|
+ var line_no = iter.get_line();
|
|
|
+ source_buffer.get_iter_at_line(out iter, line_no);
|
|
|
+ for(var i = 0; i < level; i++) {
|
|
|
+ source_buffer.insert(ref iter, "#", 1);
|
|
|
+ }
|
|
|
+ source_buffer.insert(ref iter, " ", 1);
|
|
|
+ iter.forward_line();
|
|
|
+ iter.backward_char();
|
|
|
+ source_buffer.place_cursor(iter);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void format_line_quote() {
|
|
|
+ TextIter iter;
|
|
|
+ source_buffer.get_iter_at_offset(out iter, source_buffer.cursor_position);
|
|
|
+ var line_no = iter.get_line();
|
|
|
+ source_buffer.get_iter_at_line(out iter, line_no);
|
|
|
+ source_buffer.insert(ref iter, "> ", 2);
|
|
|
+ iter.forward_line();
|
|
|
+ iter.backward_char();
|
|
|
+ source_buffer.place_cursor(iter);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void format_line_preformatted() {
|
|
|
+ TextIter iter;
|
|
|
+ source_buffer.get_iter_at_offset(out iter, source_buffer.cursor_position);
|
|
|
+ var line_no = iter.get_line();
|
|
|
+ source_buffer.get_iter_at_line(out iter, line_no);
|
|
|
+ source_buffer.insert(ref iter, "```\n", 4);
|
|
|
+ iter.forward_line();
|
|
|
+ iter.backward_char();
|
|
|
+ source_buffer.insert(ref iter, "\n```", 4);
|
|
|
+ iter.forward_line();
|
|
|
+ iter.backward_chars(5);
|
|
|
+ source_buffer.place_cursor(iter);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void format_text_bold() {
|
|
|
+ TextIter iter_start;
|
|
|
+ TextIter iter_end;
|
|
|
+ if(source_buffer.has_selection) {
|
|
|
+ source_buffer.get_selection_bounds(out iter_start, out iter_end);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ source_buffer.get_iter_at_offset(out iter_start, source_buffer.cursor_position);
|
|
|
+ source_buffer.get_iter_at_offset(out iter_end, source_buffer.cursor_position);
|
|
|
+ if(!iter_start.starts_word()) {
|
|
|
+ iter_start.backward_word_start();
|
|
|
+ }
|
|
|
+ if(!iter_end.ends_word()) {
|
|
|
+ iter_end.forward_word_end();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ var difference = iter_end.get_offset() - iter_start.get_offset();
|
|
|
+ source_buffer.insert(ref iter_start, "**", 2);
|
|
|
+ iter_start.forward_chars(difference);
|
|
|
+ source_buffer.insert(ref iter_start, "**", 2);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void format_text_unbold() {
|
|
|
+ TextIter iter;
|
|
|
+ source_buffer.get_iter_at_offset(out iter, source_buffer.cursor_position);
|
|
|
+ source_buffer.iter_forward_to_context_class_toggle(ref iter, "b");
|
|
|
+ var end_pos = iter.get_offset();
|
|
|
+ source_buffer.iter_backward_to_context_class_toggle(ref iter, "b");
|
|
|
+ iter.forward_chars(2);
|
|
|
+ source_buffer.backspace(ref iter, false, true);
|
|
|
+ source_buffer.backspace(ref iter, false, true);
|
|
|
+ iter.set_offset(end_pos-2);
|
|
|
+ source_buffer.backspace(ref iter, false, true);
|
|
|
+ source_buffer.backspace(ref iter, false, true);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void toggle_bold() {
|
|
|
+ source_buffer.begin_user_action();
|
|
|
+ if(!bold_button.active) {
|
|
|
+ format_text_unbold();
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ format_text_bold();
|
|
|
+ }
|
|
|
+ source_buffer.end_user_action();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void format_text_italic() {
|
|
|
+ TextIter iter_start;
|
|
|
+ TextIter iter_end;
|
|
|
+ if(source_buffer.has_selection) {
|
|
|
+ source_buffer.get_selection_bounds(out iter_start, out iter_end);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ source_buffer.get_iter_at_offset(out iter_start, source_buffer.cursor_position);
|
|
|
+ source_buffer.get_iter_at_offset(out iter_end, source_buffer.cursor_position);
|
|
|
+ if(!iter_start.starts_word()) {
|
|
|
+ iter_start.backward_word_start();
|
|
|
+ }
|
|
|
+ if(!iter_end.ends_word()) {
|
|
|
+ iter_end.forward_word_end();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ var difference = iter_end.get_offset() - iter_start.get_offset();
|
|
|
+ source_buffer.insert(ref iter_start, "*", 1);
|
|
|
+ iter_start.forward_chars(difference);
|
|
|
+ source_buffer.insert(ref iter_start, "*", 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void format_text_unitalic() {
|
|
|
+ TextIter iter;
|
|
|
+ source_buffer.get_iter_at_offset(out iter, source_buffer.cursor_position);
|
|
|
+ source_buffer.iter_forward_to_context_class_toggle(ref iter, "i");
|
|
|
+ var end_pos = iter.get_offset();
|
|
|
+ source_buffer.iter_backward_to_context_class_toggle(ref iter, "i");
|
|
|
+ iter.forward_chars(1);
|
|
|
+ source_buffer.backspace(ref iter, false, true);
|
|
|
+ iter.set_offset(end_pos-1);
|
|
|
+ source_buffer.backspace(ref iter, false, true);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void toggle_italic() {
|
|
|
+ source_buffer.begin_user_action();
|
|
|
+ if(!italic_button.active) {
|
|
|
+ format_text_unitalic();
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ format_text_italic();
|
|
|
+ }
|
|
|
+ source_buffer.end_user_action();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void insert_link_clicked() {
|
|
|
+ string selected = null;
|
|
|
+ if(source_buffer.has_selection) {
|
|
|
+ TextIter iter_start;
|
|
|
+ TextIter iter_end;
|
|
|
+ source_buffer.get_selection_bounds(out iter_start, out iter_end);
|
|
|
+ selected = iter_start.get_text(iter_end);
|
|
|
+ }
|
|
|
+
|
|
|
+ var win = new AddLinkWindow(selected);
|
|
|
+ win.insert_link.connect(link_insert);
|
|
|
+ win.set_transient_for(window);
|
|
|
+ win.modal = true;
|
|
|
+ win.present();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void link_insert(string url, string? label) {
|
|
|
+ TextIter iter;
|
|
|
+ source_buffer.begin_user_action();
|
|
|
+ if(source_buffer.has_selection) {
|
|
|
+ TextIter iter_end;
|
|
|
+ source_buffer.get_selection_bounds(out iter, out iter_end);
|
|
|
+ source_buffer.delete(ref iter, ref iter_end);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ source_buffer.get_iter_at_offset(out iter, source_buffer.cursor_position);
|
|
|
+ }
|
|
|
+ var link = "";
|
|
|
+ if(label != null) {
|
|
|
+ link = @"[$label]($url)";
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ link = @"<$url>";
|
|
|
+ }
|
|
|
+ source_buffer.insert(ref iter, link, link.length);
|
|
|
+ source_buffer.end_user_action();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void insert_image_clicked() {
|
|
|
+ string selected = null;
|
|
|
+ if(source_buffer.has_selection) {
|
|
|
+ TextIter iter_start;
|
|
|
+ TextIter iter_end;
|
|
|
+ source_buffer.get_selection_bounds(out iter_start, out iter_end);
|
|
|
+ selected = iter_start.get_text(iter_end);
|
|
|
+ }
|
|
|
+
|
|
|
+ var win = new AddImageWindow(selected, window);
|
|
|
+ win.insert_image.connect(image_insert);
|
|
|
+ win.set_transient_for(window);
|
|
|
+ win.modal = true;
|
|
|
+ win.present();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void image_insert(string url, string label) {
|
|
|
+ TextIter iter;
|
|
|
+ source_buffer.begin_user_action();
|
|
|
+ if(source_buffer.has_selection) {
|
|
|
+ TextIter iter_end;
|
|
|
+ source_buffer.get_selection_bounds(out iter, out iter_end);
|
|
|
+ source_buffer.delete(ref iter, ref iter_end);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ source_buffer.get_iter_at_offset(out iter, source_buffer.cursor_position);
|
|
|
+ }
|
|
|
+ var link = @"";
|
|
|
+ source_buffer.insert(ref iter, link, link.length);
|
|
|
+ source_buffer.end_user_action();
|
|
|
+ }
|
|
|
|
|
|
}
|
|
|
+
|
|
|
+ public class AddLinkWindow : Adw.Window {
|
|
|
+
|
|
|
+ public signal void insert_link(string url, string? text);
|
|
|
+ private EntryRow url_entry;
|
|
|
+ private EntryRow text_entry;
|
|
|
+
|
|
|
+ public AddLinkWindow(string? text) {
|
|
|
+
|
|
|
+ title = "Insert link";
|
|
|
+ var box = new Box(Orientation.VERTICAL, 18);
|
|
|
+ var header = new Adw.HeaderBar();
|
|
|
+ header.show_end_title_buttons = false;
|
|
|
+ box.append(header);
|
|
|
+
|
|
|
+ var insert_button = new Button.with_label("Insert");
|
|
|
+ var cancel_button = new Button.with_label("Cancel");
|
|
|
+ insert_button.add_css_class("suggested-action");
|
|
|
+ insert_button.clicked.connect(() => insert());
|
|
|
+ cancel_button.clicked.connect(() => close());
|
|
|
+ header.pack_start(cancel_button);
|
|
|
+ header.pack_end(insert_button);
|
|
|
+
|
|
|
+ var group = new PreferencesGroup();
|
|
|
+ group.width_request = 400;
|
|
|
+ group.title = "Link properties";
|
|
|
+ group.margin_bottom = 18;
|
|
|
+ group.margin_end = 18;
|
|
|
+ group.margin_start = 18;
|
|
|
+
|
|
|
+ url_entry = new EntryRow ();
|
|
|
+ url_entry.title = "URI";
|
|
|
+ group.add(url_entry);
|
|
|
+
|
|
|
+ text_entry = new EntryRow ();
|
|
|
+ text_entry.title = "Label (optional)";
|
|
|
+ group.add(text_entry);
|
|
|
+
|
|
|
+ if(text != null) {
|
|
|
+ try {
|
|
|
+ Uri.is_valid(text, UriFlags.NONE);
|
|
|
+ url_entry.text = text;
|
|
|
+ }
|
|
|
+ catch (Error e) {
|
|
|
+ text_entry.text = text;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ box.append(group);
|
|
|
+ content = box;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void insert() {
|
|
|
+ string text = null;
|
|
|
+ if(text_entry.text.chomp().chug() != "" || text_entry.text == url_entry.text) {
|
|
|
+ text = text_entry.text;
|
|
|
+ }
|
|
|
+ insert_link(url_entry.text, text);
|
|
|
+ close();
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public class AddImageWindow : Adw.Window {
|
|
|
+
|
|
|
+ public signal void insert_image(string url, string text);
|
|
|
+ private FileChooserRow file_entry;
|
|
|
+ private EntryRow text_entry;
|
|
|
+
|
|
|
+ public AddImageWindow(string? text, ViewerWindow win) {
|
|
|
+
|
|
|
+ title = "Insert link";
|
|
|
+ var box = new Box(Orientation.VERTICAL, 18);
|
|
|
+ var header = new Adw.HeaderBar();
|
|
|
+ header.show_end_title_buttons = false;
|
|
|
+ box.append(header);
|
|
|
+
|
|
|
+ var insert_button = new Button.with_label("Insert");
|
|
|
+ var cancel_button = new Button.with_label("Cancel");
|
|
|
+ insert_button.add_css_class("suggested-action");
|
|
|
+ insert_button.clicked.connect(() => insert());
|
|
|
+ cancel_button.clicked.connect(() => close());
|
|
|
+ header.pack_start(cancel_button);
|
|
|
+ header.pack_end(insert_button);
|
|
|
+
|
|
|
+ var group = new PreferencesGroup();
|
|
|
+ group.width_request = 400;
|
|
|
+ group.title = "Link properties";
|
|
|
+ group.margin_bottom = 18;
|
|
|
+ group.margin_end = 18;
|
|
|
+ group.margin_start = 18;
|
|
|
+
|
|
|
+ file_entry = new FileChooserRow(win);
|
|
|
+ file_entry.title = "Image file";
|
|
|
+ file_entry.set_assets(win.publication.assets.where(a => a.mimetype.has_prefix ("image/")));
|
|
|
+ group.add(file_entry);
|
|
|
+
|
|
|
+ text_entry = new EntryRow ();
|
|
|
+ text_entry.title = "Image description";
|
|
|
+ group.add(text_entry);
|
|
|
+
|
|
|
+ if(text != null) {
|
|
|
+ text_entry.text = text;
|
|
|
+ }
|
|
|
+
|
|
|
+ box.append(group);
|
|
|
+ content = box;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void insert() {
|
|
|
+ insert_image(file_entry.selected_asset, text_entry.text);
|
|
|
+ close();
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
}
|