Restore text editor

This commit is contained in:
daleclack 2024-02-16 14:42:11 +08:00
parent 2214c2f6d6
commit d6727da3f4
8 changed files with 335 additions and 518 deletions

View File

@ -22,6 +22,11 @@
<attribute name="action">win.text_copy</attribute> <attribute name="action">win.text_copy</attribute>
<attribute name="accel">&lt;Control&gt;c</attribute> <attribute name="accel">&lt;Control&gt;c</attribute>
</item> </item>
<item>
<attribute name="label">Cut</attribute>
<attribute name="action">win.text_cut</attribute>
<attribute name="accel">&lt;Control&gt;c</attribute>
</item>
<item> <item>
<attribute name="label">Paste</attribute> <attribute name="label">Paste</attribute>
<attribute name="action">win.text_paste</attribute> <attribute name="action">win.text_paste</attribute>

View File

@ -5,3 +5,5 @@
G_DECLARE_FINAL_TYPE(TextEditor, text_editor, TEXT, EDITOR, GtkApplicationWindow) G_DECLARE_FINAL_TYPE(TextEditor, text_editor, TEXT, EDITOR, GtkApplicationWindow)
TextEditor *text_editor_new(); TextEditor *text_editor_new();
void text_editor_save_config(TextEditor *self);

View File

@ -1,72 +0,0 @@
#pragma once
#include <gtkmm.h>
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<Gtk::Builder> menu_builder, expend_builder;
// SearchBar
Gtk::SearchBar searchbar;
Gtk::SearchEntry search_entry;
Gtk::Box searchbox;
Gtk::Button search_up, search_down;
Glib::RefPtr<Glib::Binding> search_binding;
Gtk::TextIter curr_iter_up, curr_iter_down;
// Window widgets
Gtk::Box vbox, hbox, *infobox;
Gtk::ScrolledWindow sw1;
Glib::RefPtr<Gtk::TextBuffer> 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<Gtk::FileChooserNative> 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();
};

View File

@ -5,7 +5,8 @@ struct _MyInfoBar
GtkBox parent_instance; GtkBox parent_instance;
GtkWidget *btn_box; GtkWidget *btn_box;
GtkWidget *space_label; GtkWidget *space_label;
GtkWidget *revealer, *msg_label, *button; // GtkWidget *revealer;
GtkWidget *msg_label, *button;
}; };
G_DEFINE_TYPE(MyInfoBar, my_infobar, GTK_TYPE_BOX) 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) static void my_infobar_init(MyInfoBar *self)
{ {
// Create widgets // Create widgets
self->revealer = gtk_revealer_new(); // self->revealer = gtk_revealer_new();
self->btn_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); self->btn_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
self->msg_label = gtk_label_new(""); self->msg_label = gtk_label_new("");
self->space_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_halign(self->msg_label, GTK_ALIGN_START);
gtk_widget_set_valign(self->button, GTK_ALIGN_END); gtk_widget_set_valign(self->button, GTK_ALIGN_END);
gtk_widget_set_hexpand(self->space_label, TRUE); gtk_widget_set_hexpand(self->space_label, TRUE);
gtk_widget_set_margin_start(self->btn_box, 10); gtk_widget_set_margin_start(self->btn_box, 20);
gtk_widget_set_margin_end(self->btn_box, 10); 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_revealer_set_child(GTK_REVEALER(self->revealer), self->btn_box);
gtk_box_append(GTK_BOX(self), self->revealer); // gtk_box_append(GTK_BOX(self), self->revealer);
gtk_widget_set_visible(self->btn_box, FALSE);
} }
static void my_infobar_class_init(MyInfoBarClass *self) 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)); 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 // Set label for info bar
gtk_label_set_label(GTK_LABEL(info_bar->msg_label), message); gtk_label_set_label(GTK_LABEL(info_bar->msg_label), message);
gtk_widget_set_visible(info_bar->btn_box, TRUE);
} }

View File

@ -7,4 +7,4 @@ G_DECLARE_FINAL_TYPE(MyInfoBar, my_infobar, MY, INFOBAR, GtkBox)
MyInfoBar *my_infobar_new(); MyInfoBar *my_infobar_new();
// Copy message, the limit is 256 characters // 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);

View File

@ -1,433 +0,0 @@
#include "TextEditor.hh"
#include "text_types.hh"
#include "../json_nlohmann/json.hpp"
#include <fstream>
#include <iostream>
#include <string>
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<Gio::Menu>::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<Gtk::Box *>(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);
}

View File

@ -1,4 +1,10 @@
#include "TextEditor.h" #include "TextEditor.h"
#include "MyInfoBar.h"
#include "../json_nlohmann/json.hpp"
#include <cstring>
#include <fstream>
using json = nlohmann::json;
struct _TextEditor struct _TextEditor
{ {
@ -9,14 +15,188 @@ struct _TextEditor
GtkTextBuffer *text_buffer; GtkTextBuffer *text_buffer;
GtkWidget *menu_btn, *popover; GtkWidget *menu_btn, *popover;
GtkBuilder *menu_builder, *expander_builder; GtkBuilder *menu_builder, *expander_builder;
// Input keyboard
GtkWidget *expander; GtkWidget *expander;
GtkWidget *btns[26]; GtkWidget *btns[26];
GtkWidget *btnshift, *btntab, *btn_caps, *btnenter; 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; int width, height;
char file_path[PATH_MAX] = {0};
}; };
G_DEFINE_TYPE(TextEditor, text_editor, GTK_TYPE_APPLICATION_WINDOW) 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) static void text_editor_btn_clicked(GtkButton *btn, TextEditor *self)
{ {
char tmp[2] = {0}; char tmp[2] = {0};
@ -28,7 +208,9 @@ static void text_editor_btn_clicked(GtkButton *btn, TextEditor *self)
{ {
tmp[0] = label[0]; tmp[0] = label[0];
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->btnshift), FALSE); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->btnshift), FALSE);
}else{ }
else
{
tmp[0] = label[0] + 32; 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); 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) 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 // Use headerbar for title and more info
self->header = gtk_header_bar_new(); self->header = gtk_header_bar_new();
gtk_window_set_title(GTK_WINDOW(self), "Text editor"); gtk_window_set_title(GTK_WINDOW(self), "Text editor");
gtk_window_set_titlebar(GTK_WINDOW(self), self->header); 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 // Add menu button
self->menu_btn = gtk_menu_button_new(); 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->main_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
self->scrolled_win = gtk_scrolled_window_new(); self->scrolled_win = gtk_scrolled_window_new();
// Add info area // 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 // Create text view
self->text_view = gtk_text_view_new(); self->text_view = gtk_text_view_new();
@ -116,3 +404,25 @@ TextEditor *text_editor_new()
{ {
return TEXT_EDITOR(g_object_new(text_editor_get_type(), NULL)); return TEXT_EDITOR(g_object_new(text_editor_get_type(), NULL));
} }
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();
}

View File

@ -421,6 +421,7 @@ static void btnedit_clicked(GtkWidget *widget, MyDock *dock)
static gboolean edit_win_closed(GtkWidget *win, MyDock *dock) static gboolean edit_win_closed(GtkWidget *win, MyDock *dock)
{ {
// Hide the window // Hide the window
text_editor_save_config(TEXT_EDITOR(win));
gtk_widget_set_visible(win, FALSE); gtk_widget_set_visible(win, FALSE);
gtk_image_set_from_icon_name(GTK_IMAGE(dock->image_edit), "my_textedit"); gtk_image_set_from_icon_name(GTK_IMAGE(dock->image_edit), "my_textedit");
return TRUE; return TRUE;