diff --git a/Gtk4_Reset/res/text_menu.xml b/Gtk4_Reset/res/text_menu.xml
index ae02db2..491f57e 100644
--- a/Gtk4_Reset/res/text_menu.xml
+++ b/Gtk4_Reset/res/text_menu.xml
@@ -22,6 +22,11 @@
win.text_copy
<Control>c
+ -
+ Cut
+ win.text_cut
+ <Control>c
+
-
Paste
win.text_paste
diff --git a/Gtk4_Reset/src/apps/TextEditor.h b/Gtk4_Reset/src/apps/TextEditor.h
index 69fc9b0..7630202 100644
--- a/Gtk4_Reset/src/apps/TextEditor.h
+++ b/Gtk4_Reset/src/apps/TextEditor.h
@@ -5,3 +5,5 @@
G_DECLARE_FINAL_TYPE(TextEditor, text_editor, TEXT, EDITOR, GtkApplicationWindow)
TextEditor *text_editor_new();
+
+void text_editor_save_config(TextEditor *self);
diff --git a/Gtk4_Reset/src/apps/TextEditor.hh b/Gtk4_Reset/src/apps/TextEditor.hh
deleted file mode 100644
index 2238cf0..0000000
--- a/Gtk4_Reset/src/apps/TextEditor.hh
+++ /dev/null
@@ -1,72 +0,0 @@
-#pragma once
-
-#include
-
-class TextEditor : public Gtk::ApplicationWindow
-{
-public:
- TextEditor();
-
-private:
- // Header widgets
- Gtk::HeaderBar header;
- Gtk::MenuButton menubtn;
- Gtk::Popover popover;
- Gtk::ToggleButton search_button;
- Glib::RefPtr menu_builder, expend_builder;
-
- // SearchBar
- Gtk::SearchBar searchbar;
- Gtk::SearchEntry search_entry;
- Gtk::Box searchbox;
- Gtk::Button search_up, search_down;
- Glib::RefPtr search_binding;
- Gtk::TextIter curr_iter_up, curr_iter_down;
-
- // Window widgets
- Gtk::Box vbox, hbox, *infobox;
- Gtk::ScrolledWindow sw1;
- Glib::RefPtr buffer1;
- Gtk::TextView textview1;
- Gtk::InfoBar infobar;
- Gtk::Label label1;
- Gtk::Expander *expender;
- Gtk::Button *btns[26], *btntab, *btnenter;
- Gtk::ToggleButton *btnshift, *btncaps;
-
- // File Dialog
- Glib::RefPtr dialog;
- Glib::ustring curr_filename;
- bool file_opened;
-
- // Signal Handlers
- bool window_delete_event(GdkEventAny *event);
-
- // File Operation functions
- void btnopen_clicked();
- void opendialog_response(int response);
- void btnsave_clicked();
- void btnsaveas_clicked();
- void savedialog_response(int response);
-
- // Copy, Paste and text operations
- void btncopy_clicked();
- void btnpaste_clicked();
- void btnclose_clicked();
- void buffer1_changed();
- void clipboard_receive(const Glib::ustring &text);
- void infobar_response(int response);
-
- // Search funtion
- void search_entry_changed();
- void search_forward();
- void search_backward();
-
- // Keyboard press
- void key_pressed(Gtk::Button *button);
- void btntab_clicked();
- void btnenter_clicked();
-
- // Other Signal Handlers
- void about_activated();
-};
diff --git a/Gtk4_Reset/src/text_app/MyInfoBar.cpp b/Gtk4_Reset/src/text_app/MyInfoBar.cpp
index 6731edd..1705fd7 100644
--- a/Gtk4_Reset/src/text_app/MyInfoBar.cpp
+++ b/Gtk4_Reset/src/text_app/MyInfoBar.cpp
@@ -5,7 +5,8 @@ struct _MyInfoBar
GtkBox parent_instance;
GtkWidget *btn_box;
GtkWidget *space_label;
- GtkWidget *revealer, *msg_label, *button;
+ // GtkWidget *revealer;
+ GtkWidget *msg_label, *button;
};
G_DEFINE_TYPE(MyInfoBar, my_infobar, GTK_TYPE_BOX)
@@ -19,7 +20,7 @@ static void my_infobar_btn_clicked(GtkButton *btn, MyInfoBar *self)
static void my_infobar_init(MyInfoBar *self)
{
// Create widgets
- self->revealer = gtk_revealer_new();
+ // self->revealer = gtk_revealer_new();
self->btn_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
self->msg_label = gtk_label_new("");
self->space_label = gtk_label_new(" ");
@@ -35,11 +36,13 @@ static void my_infobar_init(MyInfoBar *self)
gtk_widget_set_halign(self->msg_label, GTK_ALIGN_START);
gtk_widget_set_valign(self->button, GTK_ALIGN_END);
gtk_widget_set_hexpand(self->space_label, TRUE);
- gtk_widget_set_margin_start(self->btn_box, 10);
- gtk_widget_set_margin_end(self->btn_box, 10);
+ gtk_widget_set_margin_start(self->btn_box, 20);
+ gtk_widget_set_margin_end(self->btn_box, 30);
+ gtk_box_prepend(GTK_BOX(self), self->btn_box);
- gtk_revealer_set_child(GTK_REVEALER(self->revealer), self->btn_box);
- gtk_box_append(GTK_BOX(self), self->revealer);
+ // gtk_revealer_set_child(GTK_REVEALER(self->revealer), self->btn_box);
+ // gtk_box_append(GTK_BOX(self), self->revealer);
+ gtk_widget_set_visible(self->btn_box, FALSE);
}
static void my_infobar_class_init(MyInfoBarClass *self)
@@ -51,8 +54,9 @@ MyInfoBar *my_infobar_new()
return MY_INFOBAR(g_object_new(my_infobar_get_type(), NULL));
}
-void my_info_bar_set_message(MyInfoBar *info_bar, const char *message)
+void my_infobar_show_message(MyInfoBar *info_bar, const char *message)
{
// Set label for info bar
gtk_label_set_label(GTK_LABEL(info_bar->msg_label), message);
+ gtk_widget_set_visible(info_bar->btn_box, TRUE);
}
diff --git a/Gtk4_Reset/src/text_app/MyInfoBar.h b/Gtk4_Reset/src/text_app/MyInfoBar.h
index 9afd26b..abc96d0 100644
--- a/Gtk4_Reset/src/text_app/MyInfoBar.h
+++ b/Gtk4_Reset/src/text_app/MyInfoBar.h
@@ -7,4 +7,4 @@ G_DECLARE_FINAL_TYPE(MyInfoBar, my_infobar, MY, INFOBAR, GtkBox)
MyInfoBar *my_infobar_new();
// Copy message, the limit is 256 characters
-void my_info_bar_set_message(MyInfoBar *info_bar, const char *message);
+void my_infobar_show_message(MyInfoBar *info_bar, const char *message);
diff --git a/Gtk4_Reset/src/text_app/TextEditor.cc b/Gtk4_Reset/src/text_app/TextEditor.cc
deleted file mode 100644
index c0a85f0..0000000
--- a/Gtk4_Reset/src/text_app/TextEditor.cc
+++ /dev/null
@@ -1,433 +0,0 @@
-#include "TextEditor.hh"
-#include "text_types.hh"
-#include "../json_nlohmann/json.hpp"
-#include
-#include
-#include
-
-using json = nlohmann::json;
-
-TextEditor::TextEditor()
- : vbox(Gtk::Orientation::VERTICAL, 5),
- hbox(Gtk::Orientation::HORIZONTAL, 5),
- searchbox(Gtk::Orientation::HORIZONTAL, 5),
- file_opened(false)
-{
- // Load window config from json file
- int width = 800, height = 450;
- std::ifstream json_file("text_config.json");
- if (json_file.is_open())
- {
- json data = json::parse(json_file);
- width = data["width"];
- height = data["height"];
- }
- json_file.close();
-
- // Initalize Window
- set_default_size(width, height);
- set_icon_name("my_textedit");
-
- // Initalize HeaderBar
- header.set_decoration_layout("close,minimize,maximize:menu");
- header.set_show_close_button();
- menubtn.set_image_from_icon_name("open-menu");
- search_button.set_image_from_icon_name("find");
- header.prepend(menubtn);
- header.prepend(search_button);
- header.set_title("Simple Text Editor");
- set_titlebar(header);
-
- // Add a menu
- menu_builder = Gtk::Builder::create_from_resource("/org/gtk/daleclack/text_menu.xml");
- auto object = menu_builder->get_object("text_menu");
- auto gmenu = Glib::RefPtr::cast_dynamic(object);
- popover.bind_model(gmenu);
- menubtn.set_popover(popover);
-
- // Initalize Text Buffers
- buffer1 = textview1.get_buffer();
- buffer1->signal_changed().connect(sigc::mem_fun(*this, &TextEditor::buffer1_changed));
-
- // Pack Widgets
- sw1.set_policy(Gtk::PolicyType::AUTOMATIC, Gtk::PolicyType::AUTOMATIC);
- sw1.add(textview1);
- hbox.append(sw1);
-
- // Add actions and signal handlers
- add_action("text_open", sigc::mem_fun(*this, &TextEditor::btnopen_clicked));
- add_action("text_save", sigc::mem_fun(*this, &TextEditor::btnsave_clicked));
- add_action("text_saveas", sigc::mem_fun(*this, &TextEditor::btnsaveas_clicked));
- add_action("text_copy", sigc::mem_fun(*this, &TextEditor::btncopy_clicked));
- add_action("text_paste", sigc::mem_fun(*this, &TextEditor::btnpaste_clicked));
- add_action("text_close", sigc::mem_fun(*this, &TextEditor::btnclose_clicked));
- add_action("text_about", sigc::mem_fun(*this, &TextEditor::about_activated));
-
- // Add searchbar and search up and down buttons
- search_up.set_image_from_icon_name("up");
- search_down.set_image_from_icon_name("down");
-
- // Bind property and signals
- search_binding = Glib::Binding::bind_property(search_button.property_active(),
- searchbar.property_search_mode_enabled(),
- Glib::BINDING_BIDIRECTIONAL);
- search_entry.signal_changed().connect(sigc::mem_fun(*this, &TextEditor::search_entry_changed));
- search_up.signal_clicked().connect(sigc::mem_fun(*this, &TextEditor::search_backward));
- search_down.signal_clicked().connect(sigc::mem_fun(*this, &TextEditor::search_forward));
-
- // Pack widgets
- searchbox.append(search_entry);
- searchbox.append(search_up);
- searchbox.append(search_down);
- searchbar.add(searchbox);
- vbox.append(searchbar);
-
- // A InfoBar
- infobar.add_button("OK", Gtk::RESPONSE_OK);
- infobar.signal_response().connect(sigc::mem_fun(*this, &TextEditor::infobar_response));
- infobox = dynamic_cast(infobar.get_content_area());
- infobox->append(label1);
- vbox.append(infobar);
- vbox.append(hbox);
-
- // Save config when the window is closed
- signal_delete_event().connect(sigc::mem_fun(*this, &TextEditor::window_delete_event));
-
- // Add Intergated keyboard
- expend_builder = Gtk::Builder::create_from_resource("/org/gtk/daleclack/expender.ui");
- expend_builder->get_widget("key_expend", expender);
- expend_builder->get_widget("btnshift", btnshift);
- expend_builder->get_widget("btn_caps", btncaps);
- expend_builder->get_widget("btntab", btntab);
- expend_builder->get_widget("btnenter", btnenter);
- vbox.append(*expender);
-
- // Get alphabet buttons
- for(int i = 0; i < 26; i++){
- char name[10];
- sprintf(name, "btn%d", i);
- expend_builder->get_widget(name, btns[i]);
- btns[i]->signal_clicked().connect(sigc::bind(
- sigc::mem_fun(*this, &TextEditor::key_pressed),
- btns[i]
- ));
- }
- btntab->signal_clicked().connect(sigc::mem_fun(*this, &TextEditor::btntab_clicked));
- btnenter->signal_clicked().connect(sigc::mem_fun(*this, &TextEditor::btnenter_clicked));
-
- // Show everything
- add(vbox);
- show_all_children();
- infobar.hide();
-}
-
-void TextEditor::key_pressed(Gtk::Button *button){
- auto label = button->get_label();
- Glib::ustring::size_type pos = 0,len = 1;
- char buf[2];
- if(btncaps->get_active() || btnshift->get_active()){
- btnshift->set_active(false);
- }else{
- sprintf(buf, "%c", label[0] + 32);
- label.replace(pos, len, buf);
- }
- //std::cout << label << std::endl;
- buffer1->insert_at_cursor(label);
-}
-
-void TextEditor::btntab_clicked(){
- buffer1->insert_at_cursor("\t");
-}
-
-void TextEditor::btnenter_clicked(){
- buffer1->insert_at_cursor("\n");
-}
-
-bool TextEditor::window_delete_event(GdkEventAny *event)
-{
- // Create json raw data
- json data = json::parse(R"({
- "width":800,
- "height":450
- })");
-
- // Override config in json file
- data["width"] = sw1.get_width();
- data["height"] = sw1.get_height();
-
- // Output json data to file
- std::fstream outfile;
- outfile.open("config.json", std::ios_base::out);
- if (outfile.is_open())
- {
- outfile << data;
- }
- outfile.close();
- return false;
-}
-
-void TextEditor::btnopen_clicked()
-{
- // Create a dialog
- dialog = Gtk::FileChooserNative::create("Open a text file", *this,
- Gtk::FILE_CHOOSER_ACTION_OPEN, "OK", "Cancel");
-
- dialog->signal_response().connect(sigc::mem_fun(*this, &TextEditor::opendialog_response));
-
- // Add Filters
- auto filter = Gtk::FileFilter::create();
- filter->set_name("Text Files");
- if (mimetype_supported())
- {
- filter->add_mime_type("text/*");
- }
- else
- {
- for (int i = 0; text_globs[i] != NULL; i++)
- {
- const char *glob = text_globs[i];
- filter->add_pattern(glob);
- }
- }
- dialog->add_filter(filter);
-
- auto filter_any = Gtk::FileFilter::create();
- filter_any->set_name("Any Files");
- filter_any->add_pattern("*");
- dialog->add_filter(filter_any);
-
- dialog->show();
-}
-
-void TextEditor::opendialog_response(int response)
-{
- if (response == Gtk::RESPONSE_ACCEPT)
- {
- // Load Contents of a file
- auto file = dialog->get_file();
- curr_filename = file->get_path();
- file_opened = true;
- char *contents;
- gsize length;
- if (file->load_contents(contents, length))
- {
- buffer1->set_text(contents);
- }
- }
- dialog.reset();
-}
-
-void TextEditor::btnsave_clicked()
-{
- if (file_opened)
- {
- // Get Text
- Glib::ustring text;
- text = buffer1->get_text();
- // Set file opened to true to use save mode
- file_opened = true;
- // Save to a file
- std::ofstream outfile;
- outfile.open(curr_filename, std::ios_base::out);
- outfile << text;
- outfile.close();
- }
-}
-
-void TextEditor::btnsaveas_clicked()
-{
- // Create a dialog
- dialog = Gtk::FileChooserNative::create("Save file", *this,
- Gtk::FILE_CHOOSER_ACTION_SAVE, "OK", "Cancel");
-
- dialog->signal_response().connect(sigc::mem_fun(*this, &TextEditor::savedialog_response));
-
- // Add Filters
- auto filter = Gtk::FileFilter::create();
- filter->set_name("Text Files");
- if (mimetype_supported())
- {
- filter->add_mime_type("text/*");
- }
- else
- {
- for (int i = 0; text_globs[i] != NULL; i++)
- {
- const char *glob = text_globs[i];
- filter->add_pattern(glob);
- }
- }
- dialog->add_filter(filter);
-
- auto filter_any = Gtk::FileFilter::create();
- filter_any->set_name("Any Files");
- filter_any->add_pattern("*");
- dialog->add_filter(filter_any);
-
- dialog->show();
-}
-
-void TextEditor::savedialog_response(int response)
-{
- if (response == Gtk::RESPONSE_ACCEPT)
- {
- // Get Filename
- auto file = dialog->get_file();
- std::string filename = file->get_path();
- // Get Text
- Glib::ustring text;
- text = buffer1->get_text();
- // Save to a file
- std::ofstream outfile;
- outfile.open(filename, std::ios_base::out);
- outfile << text;
- outfile.close();
- }
- dialog.reset();
-}
-
-void TextEditor::buffer1_changed()
-{
- // When the text changed,enable the copy button
-}
-
-void TextEditor::search_entry_changed()
-{
- // Get Text to search
- const Glib::ustring text = search_entry.get_text();
-
- Gtk::TextIter start, end;
- // If get text to search, select the text and storage the position
- if (text.length() != 0)
- {
- if (buffer1->begin().forward_search(text, Gtk::TEXT_SEARCH_CASE_INSENSITIVE, start, end))
- {
- curr_iter_up = start;
- curr_iter_down = end;
- buffer1->select_range(start, end);
- textview1.scroll_to(start);
- }
- }
-}
-
-void TextEditor::search_forward()
-{
- // Get Text to search
- const Glib::ustring search_text = search_entry.get_text();
-
- Gtk::TextIter start, end;
- // Get Text to search, down to the end of text
- if (search_text.length() != 0)
- {
- if (curr_iter_down.forward_search(search_text, Gtk::TEXT_SEARCH_CASE_INSENSITIVE, start, end))
- {
- curr_iter_up = start;
- curr_iter_down = end;
- buffer1->select_range(start, end);
- textview1.scroll_to(start);
- }
- }
-}
-
-void TextEditor::search_backward()
-{
- // Get Text to search, up to the start of text
- const Glib::ustring search_text = search_entry.get_text();
-
- Gtk::TextIter start, end;
- // Get Text to search
- if (search_text.length() != 0)
- {
- if (curr_iter_up.backward_search(search_text, Gtk::TEXT_SEARCH_CASE_INSENSITIVE, start, end))
- {
- curr_iter_up = start;
- curr_iter_down = end;
- buffer1->select_range(start, end);
- textview1.scroll_to(start);
- }
- }
-}
-
-void TextEditor::btncopy_clicked()
-{
- // Get Text
- Glib::ustring text;
- Gtk::TextBuffer::iterator start, end;
- if (buffer1->get_selection_bounds(start, end))
- {
- text = buffer1->get_text(start, end);
- }
- else
- {
- text = buffer1->get_text();
- }
-
- // Get Clipboard and set text
- auto refClipboard = Gtk::Clipboard::get();
- refClipboard->set_text(text);
-
- // Show InfoBar
- label1.set_label("The Text is copyed");
- infobar.show();
-}
-
-void TextEditor::btnpaste_clicked()
-{
- // Get ClipBoard
- auto refClipboard = Gtk::Clipboard::get();
- refClipboard->request_text(sigc::mem_fun(*this, &TextEditor::clipboard_receive));
-}
-
-void TextEditor::clipboard_receive(const Glib::ustring &text)
-{
- if (buffer1->insert_interactive_at_cursor(text))
- {
- // Show InfoBar
- label1.set_label("The Text is Pasted at cursor position");
- infobar.show();
- }
- else
- {
- // Show InfoBar
- label1.set_label("Text Paste Error!");
- infobar.show();
- }
-}
-
-void TextEditor::btnclose_clicked()
-{
- buffer1->set_text("");
- file_opened = false;
-}
-
-void TextEditor::infobar_response(int response)
-{
- infobar.hide();
-}
-
-void TextEditor::about_activated()
-{
- char *version, *copyright;
- // The Gtkmm Version
- version = g_strdup_printf("1.0\nRunning Against Gtkmm %d.%d.%d",
- GTKMM_MAJOR_VERSION,
- GTKMM_MINOR_VERSION,
- GTKMM_MICRO_VERSION);
- const char *authors[] = {"Dale Clack", NULL};
- // Copyright Informaion
- copyright = g_strdup_printf("© 2019—2022 The Xe Project");
- // Show the about dialog
- gtk_show_about_dialog(GTK_WINDOW(this->gobj()),
- "program-name", "Text Editot",
- "version", version,
- "copyright", copyright,
- "comments", "A simple text editor",
- "authors", authors,
- "license-type", GTK_LICENSE_GPL_3_0,
- "logo-icon-name", "org.gtk.daleclack",
- "title", "About Simple text editor",
- (char *)NULL);
- // Free memory
- g_free(version);
- g_free(copyright);
-}
diff --git a/Gtk4_Reset/src/text_app/TextEditor.cpp b/Gtk4_Reset/src/text_app/TextEditor.cpp
index c7d9c2b..58a9f2e 100644
--- a/Gtk4_Reset/src/text_app/TextEditor.cpp
+++ b/Gtk4_Reset/src/text_app/TextEditor.cpp
@@ -1,4 +1,10 @@
#include "TextEditor.h"
+#include "MyInfoBar.h"
+#include "../json_nlohmann/json.hpp"
+#include
+#include
+
+using json = nlohmann::json;
struct _TextEditor
{
@@ -9,14 +15,188 @@ struct _TextEditor
GtkTextBuffer *text_buffer;
GtkWidget *menu_btn, *popover;
GtkBuilder *menu_builder, *expander_builder;
+ // Input keyboard
GtkWidget *expander;
GtkWidget *btns[26];
GtkWidget *btnshift, *btntab, *btn_caps, *btnenter;
+
+ // Info
+ MyInfoBar *info_bar;
+
+ // Search Area
+ GtkWidget *btnsearch;
+ GtkWidget *search_box;
+ GtkWidget *search_bar, *search_entry;
+ GtkWidget *btn_up, *btn_down;
+ GtkTextIter curr_start, curr_end;
+
+ // Others
int width, height;
+ char file_path[PATH_MAX] = {0};
};
G_DEFINE_TYPE(TextEditor, text_editor, GTK_TYPE_APPLICATION_WINDOW)
+static void text_dialog_open_file(GObject *dialog, GAsyncResult *result, gpointer data)
+{
+ GFile *file;
+ TextEditor *editor = TEXT_EDITOR(data);
+ char *contents;
+ gsize length;
+
+ // Get file to open
+ file = gtk_file_dialog_open_finish(GTK_FILE_DIALOG(dialog), result, NULL);
+ if (file != NULL)
+ {
+ // If file opened, try to load contents
+ if (g_file_load_contents(file, NULL, &contents, &length, NULL, NULL))
+ {
+ gtk_text_buffer_set_text(editor->text_buffer, contents, length);
+
+ // Load path for file opened
+ auto path = g_file_get_path(file);
+ strncpy(editor->file_path, path, PATH_MAX);
+ g_free(path);
+ }
+ g_object_unref(file);
+ g_free(contents);
+ }
+}
+
+static void text_editor_open_activated(GSimpleAction *action,
+ GVariant *parmeter,
+ gpointer data)
+{
+ TextEditor *editor = TEXT_EDITOR(data);
+ // Create GtkFileDialog
+ GtkFileDialog *dialog = gtk_file_dialog_new();
+ gtk_file_dialog_open(dialog, GTK_WINDOW(editor), NULL, text_dialog_open_file, editor);
+}
+
+static char *text_editor_get_text(TextEditor *editor)
+{
+ // Get Content of text buffer
+ GtkTextIter start_iter, end_iter;
+ gtk_text_buffer_get_start_iter(editor->text_buffer, &start_iter);
+ gtk_text_buffer_get_end_iter(editor->text_buffer, &end_iter);
+ char *content = gtk_text_buffer_get_text(editor->text_buffer, &start_iter, &end_iter, TRUE);
+ return content;
+}
+
+static void text_dialog_save_file(GObject *dialog, GAsyncResult *result, gpointer data)
+{
+ GFile *file;
+ TextEditor *editor = TEXT_EDITOR(data);
+
+ // Get Text
+ char *content = text_editor_get_text(editor);
+
+ // Open the file to save text
+ file = gtk_file_dialog_save_finish(GTK_FILE_DIALOG(dialog), result, NULL);
+ if (file != NULL)
+ {
+ char *path = g_file_get_path(file);
+ if (g_file_set_contents(path, content, strlen(content), NULL))
+ {
+ // Update path and show infomation
+ strncpy(editor->file_path, path, PATH_MAX);
+ my_infobar_show_message(editor->info_bar, "Content Saved!");
+ }
+ }
+}
+
+static void text_editor_save_activated(GSimpleAction *action,
+ GVariant *parmeter,
+ gpointer data)
+{
+ TextEditor *editor = TEXT_EDITOR(data);
+
+ // Check the file for content
+ if (strlen(editor->file_path) != 0)
+ {
+ char *content = text_editor_get_text(editor);
+ // Save content to opened file
+ if (g_file_set_contents(editor->file_path, content, strlen(content), NULL))
+ {
+ my_infobar_show_message(editor->info_bar, "Content saved!");
+ }
+ g_free(content);
+ }
+ else
+ {
+ // Open a file dialog to save file
+ GtkFileDialog *dialog = gtk_file_dialog_new();
+ gtk_file_dialog_save(dialog, GTK_WINDOW(editor), NULL, text_dialog_save_file, editor);
+ }
+}
+
+static void text_editor_saveas_activated(GSimpleAction *action,
+ GVariant *parmeter,
+ gpointer data)
+{
+ // Open a file dialog to save file
+ GtkFileDialog *dialog = gtk_file_dialog_new();
+ gtk_file_dialog_save(dialog, GTK_WINDOW(data), NULL, text_dialog_save_file, data);
+}
+
+static void text_editor_copy_activated(GSimpleAction *action,
+ GVariant *parmeter,
+ gpointer data)
+{
+ TextEditor *editor = TEXT_EDITOR(data);
+
+ // Get ClipBoard
+ GdkClipboard *clipboard = gtk_widget_get_clipboard(editor->text_view);
+
+ // Get Selected text
+ gtk_text_buffer_copy_clipboard(editor->text_buffer, clipboard);
+ my_infobar_show_message(editor->info_bar, "Text Copied!");
+}
+
+static void text_editor_cut_activated(GSimpleAction *action,
+ GVariant *parmeter,
+ gpointer data)
+{
+ TextEditor *editor = TEXT_EDITOR(data);
+
+ // Get ClipBoard
+ GdkClipboard *clipboard = gtk_widget_get_clipboard(editor->text_view);
+
+ // Get Selected text
+ gtk_text_buffer_cut_clipboard(editor->text_buffer, clipboard, TRUE);
+ my_infobar_show_message(editor->info_bar, "Text Cutted!");
+}
+
+static void text_editor_paste_activated(GSimpleAction *action,
+ GVariant *parmeter,
+ gpointer data)
+{
+ TextEditor *editor = TEXT_EDITOR(data);
+
+ // Get ClipBoard
+ GdkClipboard *clipboard = gtk_widget_get_clipboard(editor->text_view);
+
+ // Get Selected text
+ GtkTextIter insert_iter;
+ gtk_text_buffer_paste_clipboard(editor->text_buffer, clipboard, NULL, TRUE);
+ my_infobar_show_message(editor->info_bar, "Text Pasted!");
+}
+
+static void text_editor_close_activated(GSimpleAction *action,
+ GVariant *parmeter,
+ gpointer data)
+{
+ TextEditor *editor = TEXT_EDITOR(data);
+ gtk_text_buffer_set_text(editor->text_buffer, "", 0);
+ gtk_window_close(GTK_WINDOW(data));
+}
+
+static void text_editor_about_activated(GSimpleAction *action,
+ GVariant *parmeter,
+ gpointer data)
+{
+}
+
static void text_editor_btn_clicked(GtkButton *btn, TextEditor *self)
{
char tmp[2] = {0};
@@ -28,7 +208,9 @@ static void text_editor_btn_clicked(GtkButton *btn, TextEditor *self)
{
tmp[0] = label[0];
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->btnshift), FALSE);
- }else{
+ }
+ else
+ {
tmp[0] = label[0] + 32;
}
@@ -48,13 +230,95 @@ static void text_editor_btntab_clicked(GtkButton *btn, TextEditor *self)
gtk_text_buffer_insert_at_cursor(self->text_buffer, "\t", 1);
}
+static void text_editor_search_backward(GtkWidget *widget, TextEditor *self)
+{
+ GtkTextIter start_iter, match_start, match_end;
+
+ // Get text to search
+ const char *search_text = gtk_editable_get_text(GTK_EDITABLE(self->search_entry));
+ start_iter = self->curr_start;
+ if (gtk_text_iter_backward_search(&start_iter, search_text, GTK_TEXT_SEARCH_CASE_INSENSITIVE,
+ &match_start, &match_end, NULL))
+ {
+ // Select text when search success
+ self->curr_start = match_start;
+ self->curr_end = match_end;
+ gtk_text_buffer_select_range(self->text_buffer, &match_start, &match_end);
+ gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(self->text_view), &match_start,
+ 0.0, FALSE, 0.0, 0.0);
+ }
+}
+
+static void text_editor_search_forward(GtkWidget *widget, TextEditor *self)
+{
+ GtkTextIter start_iter, match_start, match_end;
+
+ // Get text to search
+ const char *search_text = gtk_editable_get_text(GTK_EDITABLE(self->search_entry));
+ start_iter = self->curr_end;
+ if (gtk_text_iter_forward_search(&start_iter, search_text, GTK_TEXT_SEARCH_CASE_INSENSITIVE,
+ &match_start, &match_end, NULL))
+ {
+ // Select text when search success
+ self->curr_start = match_start;
+ self->curr_end = match_end;
+ gtk_text_buffer_select_range(self->text_buffer, &match_start, &match_end);
+ gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(self->text_view), &match_start,
+ 0.0, FALSE, 0.0, 0.0);
+ }
+}
+
+static void text_editor_search_changed(GtkSearchEntry *entry, TextEditor *self)
+{
+ GtkTextIter start_iter, match_start, match_end;
+
+ // Get text to search
+ const char *search_text = gtk_editable_get_text(GTK_EDITABLE(entry));
+ gtk_text_buffer_get_start_iter(self->text_buffer, &start_iter);
+ if (gtk_text_iter_forward_search(&start_iter, search_text, GTK_TEXT_SEARCH_CASE_INSENSITIVE,
+ &match_start, &match_end, NULL))
+ {
+ // Select text when search success
+ self->curr_start = match_start;
+ self->curr_end = match_end;
+ gtk_text_buffer_select_range(self->text_buffer, &match_start, &match_end);
+ gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(self->text_view), &match_start,
+ 0.0, FALSE, 0.0, 0.0);
+ }
+}
+
static void text_editor_init(TextEditor *self)
{
+ // Load window config from json file
+ int width = 800, height = 450;
+ std::ifstream json_file("text_config.json");
+ if (json_file.is_open())
+ {
+ json data = json::parse(json_file);
+ width = data["width"];
+ height = data["height"];
+ }
+ json_file.close();
+
// Use headerbar for title and more info
self->header = gtk_header_bar_new();
gtk_window_set_title(GTK_WINDOW(self), "Text editor");
gtk_window_set_titlebar(GTK_WINDOW(self), self->header);
- gtk_window_set_default_size(GTK_WINDOW(self), 800, 450);
+ gtk_window_set_default_size(GTK_WINDOW(self), width, height);
+
+ // Add Actions for menu
+ GActionEntry entries[] =
+ {
+ {"text_open", text_editor_open_activated, NULL, NULL, NULL},
+ {"text_save", text_editor_save_activated, NULL, NULL, NULL},
+ {"text_saveas", text_editor_saveas_activated, NULL, NULL, NULL},
+ {"text_copy", text_editor_copy_activated, NULL, NULL, NULL},
+ {"text_cut", text_editor_cut_activated, NULL, NULL, NULL},
+ {"text_paste", text_editor_paste_activated, NULL, NULL, NULL},
+ {"text_close", text_editor_close_activated, NULL, NULL, NULL},
+ {"text_about", text_editor_about_activated, NULL, NULL, NULL}};
+ g_action_map_add_action_entries(G_ACTION_MAP(self),
+ entries, G_N_ELEMENTS(entries), self);
// Add menu button
self->menu_btn = gtk_menu_button_new();
@@ -74,8 +338,32 @@ static void text_editor_init(TextEditor *self)
self->main_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
self->scrolled_win = gtk_scrolled_window_new();
+ // Add Search Button
+ self->btnsearch = gtk_toggle_button_new();
+ gtk_button_set_icon_name(GTK_BUTTON(self->btnsearch), "find");
+ gtk_header_bar_pack_end(GTK_HEADER_BAR(self->header), self->btnsearch);
+
+ // Add a search bar
+ self->search_bar = gtk_search_bar_new();
+ self->search_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
+ self->btn_up = gtk_button_new_from_icon_name("up");
+ self->btn_down = gtk_button_new_from_icon_name("down");
+ self->search_entry = gtk_search_entry_new();
+ gtk_box_append(GTK_BOX(self->search_box), self->search_entry);
+ gtk_box_append(GTK_BOX(self->search_box), self->btn_up);
+ gtk_box_append(GTK_BOX(self->search_box), self->btn_down);
+ gtk_search_bar_set_child(GTK_SEARCH_BAR(self->search_bar), self->search_box);
+ gtk_box_append(GTK_BOX(self->main_box), self->search_bar);
+ g_object_bind_property(self->btnsearch, "active",
+ self->search_bar, "search-mode-enabled", G_BINDING_DEFAULT);
+ g_signal_connect(self->search_entry, "search-changed",
+ G_CALLBACK(text_editor_search_changed), self);
+ g_signal_connect(self->btn_up, "clicked", G_CALLBACK(text_editor_search_backward), self);
+ g_signal_connect(self->btn_down, "clicked", G_CALLBACK(text_editor_search_forward), self);
+
// Add info area
-
+ self->info_bar = my_infobar_new();
+ gtk_box_append(GTK_BOX(self->main_box), GTK_WIDGET(self->info_bar));
// Create text view
self->text_view = gtk_text_view_new();
@@ -95,7 +383,7 @@ static void text_editor_init(TextEditor *self)
self->btnenter = GTK_WIDGET(gtk_builder_get_object(self->expander_builder, "btnenter"));
self->btnshift = GTK_WIDGET(gtk_builder_get_object(self->expander_builder, "btnshift"));
self->btntab = GTK_WIDGET(gtk_builder_get_object(self->expander_builder, "btntab"));
- for(int i = 0; i < 26; i++)
+ for (int i = 0; i < 26; i++)
{
char name[10];
sprintf(name, "btn%d", i);
@@ -115,4 +403,26 @@ static void text_editor_class_init(TextEditorClass *klass)
TextEditor *text_editor_new()
{
return TEXT_EDITOR(g_object_new(text_editor_get_type(), NULL));
-}
\ No newline at end of file
+}
+
+void text_editor_save_config(TextEditor *self)
+{
+ // Create json raw data
+ json data = json::parse(R"({
+ "width":800,
+ "height":450
+ })");
+
+ // Override config in json file
+ data["width"] = gtk_widget_get_width(self->main_box);
+ data["height"] = gtk_widget_get_height(self->main_box);
+
+ // Output json data to file
+ std::fstream outfile;
+ outfile.open("text_config.json", std::ios_base::out);
+ if (outfile.is_open())
+ {
+ outfile << data;
+ }
+ outfile.close();
+}
diff --git a/Gtk4_Reset/src/ui/MyDock.cpp b/Gtk4_Reset/src/ui/MyDock.cpp
index cbdd0cf..dea9660 100644
--- a/Gtk4_Reset/src/ui/MyDock.cpp
+++ b/Gtk4_Reset/src/ui/MyDock.cpp
@@ -421,6 +421,7 @@ static void btnedit_clicked(GtkWidget *widget, MyDock *dock)
static gboolean edit_win_closed(GtkWidget *win, MyDock *dock)
{
// Hide the window
+ text_editor_save_config(TEXT_EDITOR(win));
gtk_widget_set_visible(win, FALSE);
gtk_image_set_from_icon_name(GTK_IMAGE(dock->image_edit), "my_textedit");
return TRUE;