1 /* 2 * gnote 3 * 4 * Copyright (C) 2010-2013,2016-2017,2019-2021 Aurimas Cernius 5 * Copyright (C) 2009 Hubert Figuiere 6 * 7 * This program is free software: you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation, either version 3 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program. If not, see <http://www.gnu.org/licenses/>. 19 */ 20 21 22 #include <string.h> 23 24 #include <gtkmm/settings.h> 25 26 #include "notebuffer.hpp" 27 #include "noteeditor.hpp" 28 #include "preferences.hpp" 29 #include "undo.hpp" 30 #include "utils.hpp" 31 #include "debug.hpp" 32 #include "sharp/string.hpp" 33 34 namespace gnote { 35 NoteEditor(const Glib::RefPtr<Gtk::TextBuffer> & buffer,Preferences & preferences)36 NoteEditor::NoteEditor(const Glib::RefPtr<Gtk::TextBuffer> & buffer, Preferences & preferences) 37 : Gtk::TextView(buffer) 38 , m_preferences(preferences) 39 { 40 set_wrap_mode(Gtk::WRAP_WORD); 41 set_left_margin(default_margin()); 42 set_right_margin(default_margin()); 43 property_can_default().set_value(true); 44 45 m_preferences.signal_enable_custom_font_changed.connect(sigc::mem_fun(*this, &NoteEditor::update_custom_font_setting)); 46 m_preferences.signal_custom_font_face_changed.connect(sigc::mem_fun(*this, &NoteEditor::update_custom_font_setting)); 47 48 // query all monitored settings to get change notifications 49 bool enable_custom_font = m_preferences.enable_custom_font(); 50 auto font_string = m_preferences.custom_font_face(); 51 52 // Set Font from preference 53 if(enable_custom_font) { 54 modify_font_from_string(font_string); 55 } 56 57 // Set extra editor drag targets supported (in addition 58 // to the default TextView's various text formats)... 59 Glib::RefPtr<Gtk::TargetList> list = drag_dest_get_target_list(); 60 61 62 list->add ("text/uri-list", (Gtk::TargetFlags)0, 1); 63 list->add ("_NETSCAPE_URL", (Gtk::TargetFlags)0, 1); 64 65 signal_key_press_event().connect(sigc::mem_fun(*this, &NoteEditor::key_pressed), false); 66 signal_button_press_event().connect(sigc::mem_fun(*this, &NoteEditor::button_pressed), false); 67 68 g_signal_connect(gobj(), "paste-clipboard", G_CALLBACK(paste_started), this); 69 g_signal_connect_after(gobj(), "paste-clipboard", G_CALLBACK(paste_ended), this); 70 } 71 72 update_custom_font_setting()73 void NoteEditor::update_custom_font_setting() 74 { 75 if (m_preferences.enable_custom_font()) { 76 auto fontString = m_preferences.custom_font_face(); 77 DBG_OUT( "Switching note font to '%s'...", fontString.c_str()); 78 modify_font_from_string (fontString); 79 } 80 else { 81 DBG_OUT("Switching back to the default font"); 82 Gtk::Settings::get_default()->reset_property("gtk-font-name"); 83 } 84 } 85 86 modify_font_from_string(const Glib::ustring & fontString)87 void NoteEditor::modify_font_from_string (const Glib::ustring & fontString) 88 { 89 DBG_OUT("Switching note font to '%s'...", fontString.c_str()); 90 Gtk::Settings::get_default()->property_gtk_font_name() = fontString; 91 } 92 93 94 95 // 96 // DND Drop handling 97 // on_drag_data_received(const Glib::RefPtr<Gdk::DragContext> & context,int x,int y,const Gtk::SelectionData & selection_data,guint info,guint time)98 void NoteEditor::on_drag_data_received(const Glib::RefPtr<Gdk::DragContext> & context, 99 int x, int y, 100 const Gtk::SelectionData & selection_data, 101 guint info, guint time) 102 { 103 bool has_url = false; 104 105 auto targets = context->list_targets(); 106 for(auto target : targets) { 107 if (target == "text/uri-list" || 108 target == "_NETSCAPE_URL") { 109 has_url = true; 110 break; 111 } 112 } 113 114 if (has_url) { 115 utils::UriList uri_list(selection_data); 116 bool more_than_one = false; 117 118 // Place the cursor in the position where the uri was 119 // dropped, adjusting x,y by the TextView's VisibleRect. 120 Gdk::Rectangle rect; 121 get_visible_rect(rect); 122 int adjustedX = x + rect.get_x(); 123 int adjustedY = y + rect.get_y(); 124 Gtk::TextIter cursor; 125 get_iter_at_location (cursor, adjustedX, adjustedY); 126 get_buffer()->place_cursor (cursor); 127 128 Glib::RefPtr<Gtk::TextTag> link_tag = get_buffer()->get_tag_table()->lookup ("link:url"); 129 130 for(utils::UriList::const_iterator iter = uri_list.begin(); 131 iter != uri_list.end(); ++iter) { 132 const sharp::Uri & uri(*iter); 133 DBG_OUT("Got Dropped URI: %s", uri.to_string().c_str()); 134 Glib::ustring insert; 135 if (uri.is_file()) { 136 // URL-escape the path in case 137 // there are spaces (bug #303902) 138 insert = sharp::Uri::escape_uri_string(uri.local_path()); 139 } 140 else { 141 insert = uri.to_string (); 142 } 143 144 if (insert.empty() || sharp::string_trim(insert).empty()) 145 continue; 146 147 if (more_than_one) { 148 cursor = get_buffer()->get_iter_at_mark (get_buffer()->get_insert()); 149 150 // FIXME: The space here is a hack 151 // around a bug in the URL Regex which 152 // matches across newlines. 153 if (cursor.get_line_offset() == 0) { 154 get_buffer()->insert (cursor, " \n"); 155 } 156 else { 157 get_buffer()->insert (cursor, ", "); 158 } 159 } 160 161 get_buffer()->insert_with_tag(cursor, insert, link_tag); 162 more_than_one = true; 163 } 164 165 context->drag_finish(more_than_one, false, time); 166 } 167 else { 168 Gtk::TextView::on_drag_data_received (context, x, y, selection_data, info, time); 169 } 170 } 171 key_pressed(GdkEventKey * ev)172 bool NoteEditor::key_pressed(GdkEventKey *ev) 173 { 174 bool ret_value = false; 175 if(!get_editable()) { 176 return ret_value; 177 } 178 179 guint keyval; 180 GdkModifierType state; 181 GdkEvent *event = (GdkEvent*)ev; 182 if(!gdk_event_get_keyval(event, &keyval) || !gdk_event_get_state(event, &state)) { 183 return false; 184 } 185 switch(keyval) 186 { 187 case GDK_KEY_KP_Enter: 188 case GDK_KEY_Return: 189 // Allow opening notes with Ctrl + Enter 190 if(state != GDK_CONTROL_MASK) { 191 if(state & Gdk::SHIFT_MASK) { 192 ret_value = NoteBuffer::Ptr::cast_static(get_buffer())->add_new_line (true); 193 } 194 else { 195 ret_value = NoteBuffer::Ptr::cast_static(get_buffer())->add_new_line (false); 196 } 197 scroll_to (get_buffer()->get_insert()); 198 } 199 break; 200 case GDK_KEY_Tab: 201 ret_value = NoteBuffer::Ptr::cast_static(get_buffer())->add_tab (); 202 scroll_to (get_buffer()->get_insert()); 203 break; 204 case GDK_KEY_ISO_Left_Tab: 205 ret_value = NoteBuffer::Ptr::cast_static(get_buffer())->remove_tab (); 206 scroll_to (get_buffer()->get_insert()); 207 break; 208 case GDK_KEY_Delete: 209 if(Gdk::SHIFT_MASK != (state & Gdk::SHIFT_MASK)) { 210 ret_value = NoteBuffer::Ptr::cast_static(get_buffer())->delete_key_handler(); 211 scroll_to (get_buffer()->get_insert()); 212 } 213 break; 214 case GDK_KEY_BackSpace: 215 ret_value = NoteBuffer::Ptr::cast_static(get_buffer())->backspace_key_handler(); 216 break; 217 case GDK_KEY_Left: 218 case GDK_KEY_Right: 219 case GDK_KEY_Up: 220 case GDK_KEY_Down: 221 case GDK_KEY_End: 222 ret_value = false; 223 break; 224 default: 225 NoteBuffer::Ptr::cast_static(get_buffer())->check_selection(); 226 break; 227 } 228 229 return ret_value; 230 } 231 232 button_pressed(GdkEventButton *)233 bool NoteEditor::button_pressed (GdkEventButton * ) 234 { 235 NoteBuffer::Ptr::cast_static(get_buffer())->check_selection(); 236 return false; 237 } 238 paste_started(GtkTextView *,NoteEditor * _this)239 void NoteEditor::paste_started(GtkTextView*, NoteEditor *_this) 240 { 241 _this->on_paste_start(); 242 } 243 paste_ended(GtkTextView *,NoteEditor * _this)244 void NoteEditor::paste_ended(GtkTextView*, NoteEditor *_this) 245 { 246 _this->on_paste_end(); 247 } 248 on_paste_start()249 void NoteEditor::on_paste_start() 250 { 251 auto buffer = NoteBuffer::Ptr::cast_static(get_buffer()); 252 buffer->undoer().add_undo_action(new EditActionGroup(true)); 253 } 254 on_paste_end()255 void NoteEditor::on_paste_end() 256 { 257 auto buffer = NoteBuffer::Ptr::cast_static(get_buffer()); 258 buffer->undoer().add_undo_action(new EditActionGroup(false)); 259 } 260 261 262 } 263