From 0533c96cfe448309a056933c33967ee2eb6fed92 Mon Sep 17 00:00:00 2001 From: daleclack Date: Fri, 8 Jul 2022 13:46:12 +0800 Subject: [PATCH] Update text editor --- Gtkmm3_Mac/CMakeLists.txt | 1 + Gtkmm3_Mac/res/text_menu.xml | 40 ++++ Gtkmm3_Mac/src/apps/TextEditor.hh | 38 ++- Gtkmm3_Mac/src/text_app/TextEditor.cc | 326 ++++++++++++++++++-------- 4 files changed, 297 insertions(+), 108 deletions(-) create mode 100644 Gtkmm3_Mac/res/text_menu.xml diff --git a/Gtkmm3_Mac/CMakeLists.txt b/Gtkmm3_Mac/CMakeLists.txt index 9efa845..7d588fb 100644 --- a/Gtkmm3_Mac/CMakeLists.txt +++ b/Gtkmm3_Mac/CMakeLists.txt @@ -43,6 +43,7 @@ set(RESOURCE_LIST STRIPBLANKS game1.ui STRIPBLANKS game24.ui STRIPBLANKS calcapp.ui + STRIPBLANKS text_menu.xml style.css reset.css dock_style.css diff --git a/Gtkmm3_Mac/res/text_menu.xml b/Gtkmm3_Mac/res/text_menu.xml new file mode 100644 index 0000000..ae02db2 --- /dev/null +++ b/Gtkmm3_Mac/res/text_menu.xml @@ -0,0 +1,40 @@ + + + +
+ + Open + win.text_open + <Control>o + + + Save + win.text_save + <Control>s + + + Save As + win.text_saveas + <Control><Shift>s + + + Copy + win.text_copy + <Control>c + + + Paste + win.text_paste + <Control>v + + + Close + win.text_close + + + About + win.text_about + +
+
+
\ No newline at end of file diff --git a/Gtkmm3_Mac/src/apps/TextEditor.hh b/Gtkmm3_Mac/src/apps/TextEditor.hh index e00604f..0542277 100644 --- a/Gtkmm3_Mac/src/apps/TextEditor.hh +++ b/Gtkmm3_Mac/src/apps/TextEditor.hh @@ -2,29 +2,57 @@ #include -class TextEditor : public Gtk::Window{ +class TextEditor : public Gtk::ApplicationWindow{ public: TextEditor(); private: - //Child widgets - Gtk::Box vbox,hbox,btnbox,*infobox; + //Header widgets + Gtk::HeaderBar header; + Gtk::MenuButton menubtn; + Gtk::Popover popover; + Gtk::ToggleButton search_button; + Glib::RefPtr menu_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,sw2; Glib::RefPtr buffer1; Gtk::TextView textview1; - Gtk::Button btn_copy,btn_paste,btn_open,btn_save,btn_clear; Gtk::InfoBar infobar; Gtk::Label label1; + //File Dialog Glib::RefPtr dialog; + Glib::ustring curr_filename; + bool file_opened; + //Signal Handlers + + // 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 btnclear_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(); }; diff --git a/Gtkmm3_Mac/src/text_app/TextEditor.cc b/Gtkmm3_Mac/src/text_app/TextEditor.cc index a58ba71..89e2a22 100644 --- a/Gtkmm3_Mac/src/text_app/TextEditor.cc +++ b/Gtkmm3_Mac/src/text_app/TextEditor.cc @@ -2,81 +2,112 @@ #include "text_types.hh" #include +// Only for build in this repository +// #define text_globs supported_globs + TextEditor::TextEditor() -:vbox(Gtk::ORIENTATION_VERTICAL,5), -hbox(Gtk::ORIENTATION_HORIZONTAL,5), -btnbox(Gtk::ORIENTATION_VERTICAL,5), -btn_copy("Copy"), -btn_paste("Paste"), -btn_open("Open"), -btn_save("Save"), -btn_clear("Clear") + : vbox(Gtk::ORIENTATION_VERTICAL, 5), + hbox(Gtk::ORIENTATION_HORIZONTAL, 5), + searchbox(Gtk::ORIENTATION_HORIZONTAL, 5), + file_opened(false) { - //Initalize Window - set_default_size(800,450); + // Initalize Window + set_default_size(800, 450); set_icon_name("my_textedit"); - set_title("Simple Text Editor"); - - //Initalize Text Buffers - buffer1=textview1.get_buffer(); - buffer1->signal_changed().connect(sigc::mem_fun(*this,&TextEditor::buffer1_changed)); - //Pack Widgets - sw1.set_policy(Gtk::POLICY_AUTOMATIC,Gtk::POLICY_AUTOMATIC); + // 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.pack_end(menubtn); + header.pack_end(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::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); sw1.add(textview1); - btnbox.set_valign(Gtk::ALIGN_CENTER); - btnbox.pack_start(btn_copy,Gtk::PACK_SHRINK); - btnbox.pack_start(btn_paste,Gtk::PACK_SHRINK); - btnbox.pack_start(btn_open,Gtk::PACK_SHRINK); - btnbox.pack_start(btn_save,Gtk::PACK_SHRINK); - btnbox.pack_start(btn_clear,Gtk::PACK_SHRINK); hbox.pack_start(sw1); - hbox.pack_start(btnbox,Gtk::PACK_SHRINK); - - btn_open.signal_clicked().connect(sigc::mem_fun(*this,&TextEditor::btnopen_clicked)); - btn_save.signal_clicked().connect(sigc::mem_fun(*this,&TextEditor::btnsave_clicked)); - btn_copy.signal_clicked().connect(sigc::mem_fun(*this,&TextEditor::btncopy_clicked)); - btn_paste.signal_clicked().connect(sigc::mem_fun(*this,&TextEditor::btnpaste_clicked)); - btn_clear.signal_clicked().connect(sigc::mem_fun(*this,&TextEditor::btnclear_clicked)); - //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()); + // 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 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.pack_start(search_entry, Gtk::PACK_SHRINK); + searchbox.pack_start(search_up, Gtk::PACK_SHRINK); + searchbox.pack_start(search_down, Gtk::PACK_SHRINK); + searchbar.add(searchbox); + vbox.pack_start(searchbar, Gtk::PACK_SHRINK); + + // 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->pack_start(label1); - vbox.pack_start(infobar,Gtk::PACK_SHRINK); + vbox.pack_start(infobar, Gtk::PACK_SHRINK); - //Disable Copy button - btn_copy.set_sensitive(false); - - //Show everything + // Show everything vbox.pack_start(hbox); add(vbox); show_all_children(); infobar.hide(); } -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)); +void TextEditor::btnopen_clicked() +{ + // Create a dialog + dialog = Gtk::FileChooserNative::create("Open a text file", *this, + Gtk::FILE_CHOOSER_ACTION_OPEN, "OK", "Cancel"); - //Add Filters - auto filter=Gtk::FileFilter::create(); + 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()){ + if (mimetype_supported()) + { filter->add_mime_type("text/*"); - }else{ - for(int i = 0; text_globs != NULL && text_globs[i] != NULL; i++){ + } + else + { + for (int i = 0; text_globs != NULL && 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(); + auto filter_any = Gtk::FileFilter::create(); filter_any->set_name("Any Files"); filter_any->add_pattern("*"); dialog->add_filter(filter_any); @@ -84,40 +115,67 @@ void TextEditor::btnopen_clicked(){ dialog->show(); } -void TextEditor::opendialog_response(int response){ - if(response==Gtk::RESPONSE_ACCEPT){ - //Load Contents of a file - auto file=dialog->get_file(); - char * contents; +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)){ + if (file->load_contents(contents, length)) + { buffer1->set_text(contents); } } dialog.reset(); } -void TextEditor::btnsave_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)); +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(); + } +} - //Add Filters - auto filter=Gtk::FileFilter::create(); +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()){ + if (mimetype_supported()) + { filter->add_mime_type("text/*"); - }else{ - for(int i = 0; text_globs != NULL && text_globs[i] != NULL; i++){ + } + else + { + for (int i = 0; text_globs != NULL && 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(); + auto filter_any = Gtk::FileFilter::create(); filter_any->set_name("Any Files"); filter_any->add_pattern("*"); dialog->add_filter(filter_any); @@ -125,69 +183,131 @@ void TextEditor::btnsave_clicked(){ 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 +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 + text = buffer1->get_text(); + // Save to a file std::ofstream outfile; - outfile.open(filename,std::ios_base::out); - outfile<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 (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 (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(); + 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(); + // Get Clipboard and set text + auto refClipboard = Gtk::Clipboard::get(); refClipboard->set_text(text); - //Show InfoBar + // 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::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 +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 + } + else + { + // Show InfoBar label1.set_label("Text Paste Error!"); infobar.show(); } } -void TextEditor::btnclear_clicked(){ +void TextEditor::btnclose_clicked() +{ buffer1->set_text(""); + file_opened = false; } -void TextEditor::infobar_response(int response){ +void TextEditor::infobar_response(int response) +{ infobar.hide(); }