diff options
author | 2008-06-15 13:50:46 -0400 | |
---|---|---|
committer | 2008-06-15 13:50:46 -0400 | |
commit | be6ce0b84511e1bc43dbe97635cdb66b690575f2 (patch) | |
tree | 038c3af4e15f549a60600570443e85911f900357 | |
parent | e56e9f1c1a635bd205bfde9ff8120ad2e81274c0 (diff) | |
download | textadept-be6ce0b84511e1bc43dbe97635cdb66b690575f2.tar.gz textadept-be6ce0b84511e1bc43dbe97635cdb66b690575f2.zip |
Merged pm.c and find_replace.c into textadept.c; updated Makefile.
-rw-r--r-- | src/Makefile | 2 | ||||
-rw-r--r-- | src/find_replace.c | 149 | ||||
-rw-r--r-- | src/pm.c | 283 | ||||
-rw-r--r-- | src/textadept.c | 429 |
4 files changed, 430 insertions, 433 deletions
diff --git a/src/Makefile b/src/Makefile index be333460..3e44c52a 100644 --- a/src/Makefile +++ b/src/Makefile @@ -24,7 +24,7 @@ $(LUAOBJS): lua/*.c .c.o: g++ $(GTKFLAGS) $(INCLUDEDIRS) $(CXXFLAGS) -c $< -o $@ textadept:\ - textadept.o lua_interface.o pm.o find_replace.o \ + textadept.o lua_interface.o \ scintilla-st/gtk/LexLPeg.o scintilla-st/bin/scintilla.a \ $(LUAOBJS) g++ $(GTKLIBS) $(EXPORTLUASYMS) -DGTK $^ -o $@ diff --git a/src/find_replace.c b/src/find_replace.c deleted file mode 100644 index 8564ff75..00000000 --- a/src/find_replace.c +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2007-2008 Mitchell mitchell<att>caladbolg.net. See LICENSE. - -#include "textadept.h" - -#define attach(w, x1, x2, y1, y2, xo, yo, xp, yp) \ - gtk_table_attach(GTK_TABLE(findbox), w, x1, x2, y1, y2, xo, yo, xp, yp) -#define find_text gtk_entry_get_text(GTK_ENTRY(find_entry)) -#define repl_text gtk_entry_get_text(GTK_ENTRY(replace_entry)) -#define toggled(w) gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)) - -GtkWidget *findbox, *find_entry, *replace_entry; -GtkWidget *fnext_button, *fprev_button, *r_button, *ra_button; -GtkWidget *match_case_opt, *whole_word_opt, /**incremental_opt,*/ *lua_opt; -GtkAttachOptions - normal = static_cast<GtkAttachOptions>(GTK_SHRINK | GTK_FILL), - expand = static_cast<GtkAttachOptions>(GTK_EXPAND | GTK_FILL); - -static bool - fe_keypress(GtkWidget*, GdkEventKey *event, gpointer), - re_keypress(GtkWidget*, GdkEventKey *event, gpointer); - -static void button_clicked(GtkWidget *button, gpointer); - -/** - * Creates the Find/Replace text frame. - */ -GtkWidget* find_create_ui() { - findbox = gtk_table_new(2, 6, false); - - GtkWidget *flabel = gtk_label_new_with_mnemonic("_Find:"); - GtkWidget *rlabel = gtk_label_new_with_mnemonic("R_eplace:"); - find_entry = gtk_entry_new(); - gtk_widget_set_name(find_entry, "textadept-find-entry"); - replace_entry = gtk_entry_new(); - gtk_widget_set_name(replace_entry, "textadept-replace-entry"); - fnext_button = gtk_button_new_with_mnemonic("Find _Next"); - fprev_button = gtk_button_new_with_mnemonic("Find _Prev"); - r_button = gtk_button_new_with_mnemonic("_Replace"); - ra_button = gtk_button_new_with_mnemonic("Replace _All"); - match_case_opt = gtk_check_button_new_with_mnemonic("_Match case"); - whole_word_opt = gtk_check_button_new_with_mnemonic("_Whole word"); - //incremental_opt = gtk_check_button_new_with_mnemonic("_Incremental"); - lua_opt = gtk_check_button_new_with_mnemonic("_Lua pattern"); - - gtk_label_set_mnemonic_widget(GTK_LABEL(flabel), find_entry); - gtk_label_set_mnemonic_widget(GTK_LABEL(rlabel), replace_entry); - //gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(lua_opt), true); - - attach(find_entry, 1, 2, 0, 1, expand, normal, 5, 0); - attach(replace_entry, 1, 2, 1, 2, expand, normal, 5, 0); - attach(flabel, 0, 1, 0, 1, normal, normal, 5, 0); - attach(rlabel, 0, 1, 1, 2, normal, normal, 5, 0); - attach(fnext_button, 2, 3, 0, 1, normal, normal, 0, 0); - attach(fprev_button, 3, 4, 0, 1, normal, normal, 0, 0); - attach(r_button, 2, 3, 1, 2, normal, normal, 0, 0); - attach(ra_button, 3, 4, 1, 2, normal, normal, 0, 0); - attach(match_case_opt, 4, 5, 0, 1, normal, normal, 5, 0); - attach(whole_word_opt, 4, 5, 1, 2, normal, normal, 5, 0); - //attach(incremental_opt, 5, 6, 0, 1, normal, normal, 5, 0); - attach(lua_opt, 5, 6, 0, 1, normal, normal, 5, 0); - - g_signal_connect(G_OBJECT(find_entry), "key_press_event", - G_CALLBACK(fe_keypress), 0); - g_signal_connect(G_OBJECT(replace_entry), "key_press_event", - G_CALLBACK(re_keypress), 0); - g_signal_connect(G_OBJECT(fnext_button), "clicked", - G_CALLBACK(button_clicked), 0); - g_signal_connect(G_OBJECT(fprev_button), "clicked", - G_CALLBACK(button_clicked), 0); - g_signal_connect(G_OBJECT(r_button), "clicked", - G_CALLBACK(button_clicked), 0); - g_signal_connect(G_OBJECT(ra_button), "clicked", - G_CALLBACK(button_clicked), 0); - - GTK_WIDGET_UNSET_FLAGS(fnext_button, GTK_CAN_FOCUS); - GTK_WIDGET_UNSET_FLAGS(fprev_button, GTK_CAN_FOCUS); - GTK_WIDGET_UNSET_FLAGS(r_button, GTK_CAN_FOCUS); - GTK_WIDGET_UNSET_FLAGS(ra_button, GTK_CAN_FOCUS); - GTK_WIDGET_UNSET_FLAGS(match_case_opt, GTK_CAN_FOCUS); - GTK_WIDGET_UNSET_FLAGS(whole_word_opt, GTK_CAN_FOCUS); - //GTK_WIDGET_UNSET_FLAGS(incremental_opt, GTK_CAN_FOCUS); - GTK_WIDGET_UNSET_FLAGS(lua_opt, GTK_CAN_FOCUS); - - return findbox; -} - -/** - * Toggles the focus between the Find/Replace frame and the current Scintilla - * window. - */ -void find_toggle_focus() { - if (!GTK_WIDGET_HAS_FOCUS(findbox)) { - gtk_widget_show(findbox); - gtk_widget_grab_focus(find_entry); - } else { - gtk_widget_grab_focus(focused_editor); - gtk_widget_hide(findbox); - } -} - -/** - * Builds the integer flags for a Find/Replace depending on the options that are - * checked. - */ -static int get_flags() { - int flags = 0; - if (toggled(match_case_opt)) flags |= SCFIND_MATCHCASE; // 2 - if (toggled(whole_word_opt)) flags |= SCFIND_WHOLEWORD; // 4 - if (toggled(lua_opt)) flags |= 8; - return flags; -} - -/** - * Signal for a Find entry keypress. - * Currently handled keypresses: - * - Enter - Find next or previous. - */ -static bool fe_keypress(GtkWidget *, GdkEventKey *event, gpointer) { - // TODO: if incremental, call l_find() - if (event->keyval == 0xff0d) { - l_find(find_text, get_flags(), true); - return true; - } else return false; -} - -/** - * Signal for a Replace entry keypress. - * Currently handled keypresses: - * - Enter - Find next or previous. - */ -static bool re_keypress(GtkWidget *, GdkEventKey *event, gpointer) { - if (event->keyval == 0xff0d) { - l_find(find_text, get_flags(), true); - return true; - } else return false; -} - -/** - * Signal for a button click. - * Performs the appropriate action depending on the button clicked. - */ -static void button_clicked(GtkWidget *button, gpointer) { - if (button == ra_button) - l_find_replace_all(find_text, repl_text, get_flags()); - else if (button == r_button) { - l_find_replace(repl_text); - l_find(find_text, get_flags(), true); - } else l_find(find_text, get_flags(), button == fnext_button); -} diff --git a/src/pm.c b/src/pm.c deleted file mode 100644 index 79c8de51..00000000 --- a/src/pm.c +++ /dev/null @@ -1,283 +0,0 @@ -// Copyright 2007-2008 Mitchell mitchell<att>caladbolg.net. See LICENSE. - -#include "textadept.h" - -#define signal(o, s, c) g_signal_connect(G_OBJECT(o), s, G_CALLBACK(c), 0) - -GtkWidget *pm_view, *pm_entry, *pm_container; -GtkTreeStore *pm_store; - -static int pm_search_equal_func(GtkTreeModel *model, int col, const char *key, - GtkTreeIter *iter, gpointer); -static int pm_sort_iter_compare_func(GtkTreeModel *model, GtkTreeIter *a, - GtkTreeIter *b, gpointer); -static void pm_entry_activated(GtkWidget *widget, gpointer); -static bool pm_keypress(GtkWidget *, GdkEventKey *event, gpointer); -static void pm_row_expanded(GtkTreeView *, GtkTreeIter *iter, - GtkTreePath *path, gpointer); -static void pm_row_collapsed(GtkTreeView *, GtkTreeIter *iter, - GtkTreePath *path, gpointer); -static void pm_row_activated(GtkTreeView *, GtkTreePath *, GtkTreeViewColumn *, - gpointer); -static bool pm_button_press(GtkTreeView *, GdkEventButton *event, gpointer); -static bool pm_popup_menu(GtkWidget *, gpointer); -static void pm_menu_activate(GtkWidget *menu_item, gpointer); - -/** - * Creates the Project Manager pane. - * It consists of an entry box and a treeview called 'textadept-pm-entry' and - * 'textadept-pm-view' respectively for styling via gtkrc. The treeview model - * consists of a gdk-pixbuf for icons and markup text. - */ -GtkWidget* pm_create_ui() { - pm_container = gtk_vbox_new(false, 1); - - pm_entry = gtk_entry_new(); - gtk_widget_set_name(pm_entry, "textadept-pm-entry"); - gtk_box_pack_start(GTK_BOX(pm_container), pm_entry, false, false, 0); - - pm_store = gtk_tree_store_new(3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); - GtkTreeSortable *sortable = GTK_TREE_SORTABLE(pm_store); - gtk_tree_sortable_set_sort_column_id(sortable, 1, GTK_SORT_ASCENDING); - gtk_tree_sortable_set_sort_func(sortable, 1, pm_sort_iter_compare_func, - GINT_TO_POINTER(1), NULL); - - pm_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(pm_store)); - g_object_unref(pm_store); - gtk_widget_set_name(pm_view, "textadept-pm-view"); - gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(pm_view), false); - gtk_tree_view_set_enable_search(GTK_TREE_VIEW(pm_view), true); - gtk_tree_view_set_search_column(GTK_TREE_VIEW(pm_view), 2); - gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(pm_view), - pm_search_equal_func, NULL, NULL); - - GtkTreeViewColumn *column = gtk_tree_view_column_new(); - GtkCellRenderer *renderer; - renderer = gtk_cell_renderer_pixbuf_new(); // pixbuf - gtk_tree_view_column_pack_start(column, renderer, FALSE); - gtk_tree_view_column_set_attributes(column, renderer, "stock-id", 0, NULL); - renderer = gtk_cell_renderer_text_new(); // markup text - gtk_tree_view_column_pack_start(column, renderer, TRUE); - gtk_tree_view_column_set_attributes(column, renderer, "markup", 2, NULL); - gtk_tree_view_append_column(GTK_TREE_VIEW(pm_view), column); - - GtkWidget *scrolled = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_container_add(GTK_CONTAINER(scrolled), pm_view); - gtk_box_pack_start(GTK_BOX(pm_container), scrolled, true, true, 0); - - signal(pm_entry, "activate", pm_entry_activated); - signal(pm_entry, "key_press_event", pm_keypress); - signal(pm_view, "key_press_event", pm_keypress); - signal(pm_view, "row_expanded", pm_row_expanded); - signal(pm_view, "row_collapsed", pm_row_collapsed); - signal(pm_view, "row_activated", pm_row_activated); - signal(pm_view, "button_press_event", pm_button_press); - signal(pm_view, "popup-menu", pm_popup_menu); - return pm_container; -} - -/** - * Requests contents for a Project Manager parent node being opened. - * Since parents have a dummy child by default just to indicate they are indeed - * parents, that dummy child is removed now. - * @param iter The parent GtkTreeIter. - * @param path The parent GtkTreePath. - * @see l_pm_get_contents_for - */ -void pm_open_parent(GtkTreeIter *iter, GtkTreePath *path) { - l_pm_get_full_path(path); - if (l_pm_get_contents_for(NULL, true)) l_pm_populate(iter); - GtkTreeIter child; - char *filename; - gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pm_store), &child, iter, 0); - gtk_tree_model_get(GTK_TREE_MODEL(pm_store), &child, 1, &filename, -1); - if (strcmp(reinterpret_cast<const char*>(filename), "\0dummy") == 0) - gtk_tree_store_remove(pm_store, &child); - g_free(filename); -} - -/** - * Removes all Project Manager children from a parent node being closed. - * It does add a dummy child by default to indicate the parent is indeed a - * parent. It will be removed when the parent is opened. - * @param iter The parent GtkTreeIter. - */ -void pm_close_parent(GtkTreeIter *iter, GtkTreePath *) { - GtkTreeIter child; - gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pm_store), &child, iter, 0); - while (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(pm_store), iter)) - gtk_tree_store_remove(pm_store, &child); - gtk_tree_store_append(pm_store, &child, iter); - gtk_tree_store_set(pm_store, &child, 1, "\0dummy", -1); -} - -/** - * Performs the appropriate action on a selected Project Manager node. - * If the node is a collapsed parent, it is expanded; otherwise the parent is - * collapsed. If the node is not a parent at all, a Lua action is performed. - * @see l_pm_perform_action - */ -void pm_activate_selection() { - GtkTreeIter iter; - GtkTreePath *path; - GtkTreeViewColumn *column; - gtk_tree_view_get_cursor(GTK_TREE_VIEW(pm_view), &path, &column); - gtk_tree_model_get_iter(GTK_TREE_MODEL(pm_store), &iter, path); - if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(pm_store), &iter)) - if (gtk_tree_view_row_expanded(GTK_TREE_VIEW(pm_view), path)) - gtk_tree_view_collapse_row(GTK_TREE_VIEW(pm_view), path); - else - gtk_tree_view_expand_row(GTK_TREE_VIEW(pm_view), path, false); - else { - l_pm_get_full_path(path); - l_pm_perform_action(); - } - gtk_tree_path_free(path); -} - -/** - * Pops up a context menu for the selected Project Manager node. - * @param event The mouse button event. - * @see l_pm_popup_context_menu - */ -void pm_popup_context_menu(GdkEventButton *event) { - l_pm_popup_context_menu(event, G_CALLBACK(pm_menu_activate)); -} - -/** - * Performs a Lua action for a selected Project Manager menu item. - * @param menu_item The menu item. - * @see l_pm_perform_menu_action - */ -void pm_process_selected_menu_item(GtkWidget *menu_item) { - GtkWidget *label = gtk_bin_get_child(GTK_BIN(menu_item)); - const char *text = gtk_label_get_text(GTK_LABEL(label)); - GtkTreePath *path; - GtkTreeViewColumn *column; - gtk_tree_view_get_cursor(GTK_TREE_VIEW(pm_view), &path, &column); - l_pm_get_full_path(path); - l_pm_perform_menu_action(text); -} - -/** - * Toggles the focus between the Project Manager and the current Scintilla - * window. - */ -void pm_toggle_focus() { - gtk_widget_grab_focus( - GTK_WIDGET_HAS_FOCUS(focused_editor) ? pm_entry : focused_editor); -} - -/** - * When searching the Project Manager treeview, matches are tree items that - * contain the search text as a substring. - * @param model The GtkTreeModel for the treeview. - * @param col The column number to use for comparing search text to. - * @param key The search text. - * @param iter The GtkTreeIter for each tree node being compared. - */ -static int pm_search_equal_func(GtkTreeModel *model, int col, const char *key, - GtkTreeIter *iter, gpointer) { - const char *text; - gtk_tree_model_get(model, iter, col, &text, -1); - return strstr(text, key) == NULL; // false is really a match like strcmp -} - -/** - * Sorts the Project Manager treeview case sensitively. - * @param model The GtkTreeModel for the treeview. - * @param a The GtkTreeIter for one tree node being compared. - * @param b The GtkTreeIter for the other tree node being compared. - */ -static int pm_sort_iter_compare_func(GtkTreeModel *model, GtkTreeIter *a, - GtkTreeIter *b, gpointer) { - const char *a_text, *b_text; - gtk_tree_model_get(model, a, 1, &a_text, -1); - gtk_tree_model_get(model, b, 1, &b_text, -1); - if (a_text == NULL && b_text == NULL) return 0; - else if (a_text == NULL) return -1; - else if (b_text == NULL) return 1; - else return strcasecmp(a_text, b_text); -} - -// Signals - -/** - * Signal for the activation of the Project Manager entry. - * Requests contents for the treeview. - * @see l_pm_get_contents_for - */ -static void pm_entry_activated(GtkWidget *widget, gpointer) { - const char *entry_text = gtk_entry_get_text(GTK_ENTRY(widget)); - if (l_pm_get_contents_for(entry_text)) l_pm_populate(); -} - -/** - * Signal for a Project Manager keypress. - * Currently handled keypresses: - * - Ctrl+Tab - Refocuses the Scintilla view. - * - Escape - Refocuses the Scintilla view. - */ -static bool pm_keypress(GtkWidget *, GdkEventKey *event, gpointer) { - if (event->keyval == 0xff09 && event->state == GDK_CONTROL_MASK || - event->keyval == 0xff1b) { - gtk_widget_grab_focus(focused_editor); - return true; - } else return false; -} - -/** - * Signal for a Project Manager parent expansion. - * @see pm_open_parent - */ -static void pm_row_expanded(GtkTreeView *, GtkTreeIter *iter, - GtkTreePath *path, gpointer) { - pm_open_parent(iter, path); -} - -/** - * Signal for a Project Manager parent collapse. - * @see pm_close_parent - */ -static void pm_row_collapsed(GtkTreeView *, GtkTreeIter *iter, - GtkTreePath *path, gpointer) { - pm_close_parent(iter, path); -} - -/** - * Signal for the activation of a Project Manager node. - * @see pm_activate_selection - */ -static void pm_row_activated(GtkTreeView *, GtkTreePath *, GtkTreeViewColumn *, - gpointer) { - pm_activate_selection(); -} - -/** - * Signal for a Project Manager mouse click. - * If it is a right-click, popup a context menu for the selected node. - * @see pm_popup_context_menu - */ -static bool pm_button_press(GtkTreeView *, GdkEventButton *event, gpointer) { - if (event->type != GDK_BUTTON_PRESS || event->button != 3) return false; - pm_popup_context_menu(event); return true; -} - -/** - * Signal for popping up a Project Manager context menu. - * Typically Shift+F10 activates this event. - * @see pm_popup_context_menu - */ -static bool pm_popup_menu(GtkWidget *, gpointer) { - pm_popup_context_menu(NULL); return true; -} - -/** - * Signal for a selected Project Manager menu item. - * @see pm_process_selected_menu_item - */ -static void pm_menu_activate(GtkWidget *menu_item, gpointer) { - pm_process_selected_menu_item(menu_item); -} diff --git a/src/textadept.c b/src/textadept.c index e05acc36..3a0ca902 100644 --- a/src/textadept.c +++ b/src/textadept.c @@ -5,6 +5,7 @@ #define signal(o, s, c) g_signal_connect(G_OBJECT(o), s, G_CALLBACK(c), 0) +// Textadept GtkWidget *window, *focused_editor, *command_entry, *menubar, *statusbar, *docstatusbar; @@ -18,6 +19,38 @@ static bool w_focus(GtkWidget*, GdkEventFocus *, gpointer); static bool w_keypress(GtkWidget*, GdkEventKey *event, gpointer); static bool w_exit(GtkWidget*, GdkEventAny*, gpointer); +// Project Manager +GtkWidget *pm_view, *pm_entry, *pm_container; +GtkTreeStore *pm_store; + +static int pm_search_equal_func(GtkTreeModel *model, int col, const char *key, + GtkTreeIter *iter, gpointer); +static int pm_sort_iter_compare_func(GtkTreeModel *model, GtkTreeIter *a, + GtkTreeIter *b, gpointer); +static void pm_entry_activated(GtkWidget *widget, gpointer); +static bool pm_keypress(GtkWidget *, GdkEventKey *event, gpointer); +static void pm_row_expanded(GtkTreeView *, GtkTreeIter *iter, + GtkTreePath *path, gpointer); +static void pm_row_collapsed(GtkTreeView *, GtkTreeIter *iter, + GtkTreePath *path, gpointer); +static void pm_row_activated(GtkTreeView *, GtkTreePath *, GtkTreeViewColumn *, + gpointer); +static bool pm_button_press(GtkTreeView *, GdkEventButton *event, gpointer); +static bool pm_popup_menu(GtkWidget *, gpointer); +static void pm_menu_activate(GtkWidget *menu_item, gpointer); + +// Find/Replace +GtkWidget *findbox, *find_entry, *replace_entry; +GtkWidget *fnext_button, *fprev_button, *r_button, *ra_button; +GtkWidget *match_case_opt, *whole_word_opt, /**incremental_opt,*/ *lua_opt; +GtkAttachOptions + normal = static_cast<GtkAttachOptions>(GTK_SHRINK | GTK_FILL), + expand = static_cast<GtkAttachOptions>(GTK_EXPAND | GTK_FILL); + +static bool fe_keypress(GtkWidget*, GdkEventKey *event, gpointer); +static bool re_keypress(GtkWidget*, GdkEventKey *event, gpointer); +static void button_clicked(GtkWidget *button, gpointer); + /** * Runs Textadept. * Inits the Lua State, creates the user interface, and loads the core/init.lua @@ -430,3 +463,399 @@ static bool w_exit(GtkWidget*, GdkEventAny*, gpointer) { gtk_main_quit(); return false; } + +// Project Manager + +/** + * Creates the Project Manager pane. + * It consists of an entry box and a treeview called 'textadept-pm-entry' and + * 'textadept-pm-view' respectively for styling via gtkrc. The treeview model + * consists of a gdk-pixbuf for icons and markup text. + */ +GtkWidget* pm_create_ui() { + pm_container = gtk_vbox_new(false, 1); + + pm_entry = gtk_entry_new(); + gtk_widget_set_name(pm_entry, "textadept-pm-entry"); + gtk_box_pack_start(GTK_BOX(pm_container), pm_entry, false, false, 0); + + pm_store = gtk_tree_store_new(3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); + GtkTreeSortable *sortable = GTK_TREE_SORTABLE(pm_store); + gtk_tree_sortable_set_sort_column_id(sortable, 1, GTK_SORT_ASCENDING); + gtk_tree_sortable_set_sort_func(sortable, 1, pm_sort_iter_compare_func, + GINT_TO_POINTER(1), NULL); + + pm_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(pm_store)); + g_object_unref(pm_store); + gtk_widget_set_name(pm_view, "textadept-pm-view"); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(pm_view), false); + gtk_tree_view_set_enable_search(GTK_TREE_VIEW(pm_view), true); + gtk_tree_view_set_search_column(GTK_TREE_VIEW(pm_view), 2); + gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(pm_view), + pm_search_equal_func, NULL, NULL); + + GtkTreeViewColumn *column = gtk_tree_view_column_new(); + GtkCellRenderer *renderer; + renderer = gtk_cell_renderer_pixbuf_new(); // pixbuf + gtk_tree_view_column_pack_start(column, renderer, FALSE); + gtk_tree_view_column_set_attributes(column, renderer, "stock-id", 0, NULL); + renderer = gtk_cell_renderer_text_new(); // markup text + gtk_tree_view_column_pack_start(column, renderer, TRUE); + gtk_tree_view_column_set_attributes(column, renderer, "markup", 2, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(pm_view), column); + + GtkWidget *scrolled = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_container_add(GTK_CONTAINER(scrolled), pm_view); + gtk_box_pack_start(GTK_BOX(pm_container), scrolled, true, true, 0); + + signal(pm_entry, "activate", pm_entry_activated); + signal(pm_entry, "key_press_event", pm_keypress); + signal(pm_view, "key_press_event", pm_keypress); + signal(pm_view, "row_expanded", pm_row_expanded); + signal(pm_view, "row_collapsed", pm_row_collapsed); + signal(pm_view, "row_activated", pm_row_activated); + signal(pm_view, "button_press_event", pm_button_press); + signal(pm_view, "popup-menu", pm_popup_menu); + return pm_container; +} + +/** + * Requests contents for a Project Manager parent node being opened. + * Since parents have a dummy child by default just to indicate they are indeed + * parents, that dummy child is removed now. + * @param iter The parent GtkTreeIter. + * @param path The parent GtkTreePath. + * @see l_pm_get_contents_for + */ +void pm_open_parent(GtkTreeIter *iter, GtkTreePath *path) { + l_pm_get_full_path(path); + if (l_pm_get_contents_for(NULL, true)) l_pm_populate(iter); + GtkTreeIter child; + char *filename; + gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pm_store), &child, iter, 0); + gtk_tree_model_get(GTK_TREE_MODEL(pm_store), &child, 1, &filename, -1); + if (strcmp(reinterpret_cast<const char*>(filename), "\0dummy") == 0) + gtk_tree_store_remove(pm_store, &child); + g_free(filename); +} + +/** + * Removes all Project Manager children from a parent node being closed. + * It does add a dummy child by default to indicate the parent is indeed a + * parent. It will be removed when the parent is opened. + * @param iter The parent GtkTreeIter. + */ +void pm_close_parent(GtkTreeIter *iter, GtkTreePath *) { + GtkTreeIter child; + gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pm_store), &child, iter, 0); + while (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(pm_store), iter)) + gtk_tree_store_remove(pm_store, &child); + gtk_tree_store_append(pm_store, &child, iter); + gtk_tree_store_set(pm_store, &child, 1, "\0dummy", -1); +} + +/** + * Performs the appropriate action on a selected Project Manager node. + * If the node is a collapsed parent, it is expanded; otherwise the parent is + * collapsed. If the node is not a parent at all, a Lua action is performed. + * @see l_pm_perform_action + */ +void pm_activate_selection() { + GtkTreeIter iter; + GtkTreePath *path; + GtkTreeViewColumn *column; + gtk_tree_view_get_cursor(GTK_TREE_VIEW(pm_view), &path, &column); + gtk_tree_model_get_iter(GTK_TREE_MODEL(pm_store), &iter, path); + if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(pm_store), &iter)) + if (gtk_tree_view_row_expanded(GTK_TREE_VIEW(pm_view), path)) + gtk_tree_view_collapse_row(GTK_TREE_VIEW(pm_view), path); + else + gtk_tree_view_expand_row(GTK_TREE_VIEW(pm_view), path, false); + else { + l_pm_get_full_path(path); + l_pm_perform_action(); + } + gtk_tree_path_free(path); +} + +/** + * Pops up a context menu for the selected Project Manager node. + * @param event The mouse button event. + * @see l_pm_popup_context_menu + */ +void pm_popup_context_menu(GdkEventButton *event) { + l_pm_popup_context_menu(event, G_CALLBACK(pm_menu_activate)); +} + +/** + * Performs a Lua action for a selected Project Manager menu item. + * @param menu_item The menu item. + * @see l_pm_perform_menu_action + */ +void pm_process_selected_menu_item(GtkWidget *menu_item) { + GtkWidget *label = gtk_bin_get_child(GTK_BIN(menu_item)); + const char *text = gtk_label_get_text(GTK_LABEL(label)); + GtkTreePath *path; + GtkTreeViewColumn *column; + gtk_tree_view_get_cursor(GTK_TREE_VIEW(pm_view), &path, &column); + l_pm_get_full_path(path); + l_pm_perform_menu_action(text); +} + +/** + * Toggles the focus between the Project Manager and the current Scintilla + * window. + */ +void pm_toggle_focus() { + gtk_widget_grab_focus( + GTK_WIDGET_HAS_FOCUS(focused_editor) ? pm_entry : focused_editor); +} + +/** + * When searching the Project Manager treeview, matches are tree items that + * contain the search text as a substring. + * @param model The GtkTreeModel for the treeview. + * @param col The column number to use for comparing search text to. + * @param key The search text. + * @param iter The GtkTreeIter for each tree node being compared. + */ +static int pm_search_equal_func(GtkTreeModel *model, int col, const char *key, + GtkTreeIter *iter, gpointer) { + const char *text; + gtk_tree_model_get(model, iter, col, &text, -1); + return strstr(text, key) == NULL; // false is really a match like strcmp +} + +/** + * Sorts the Project Manager treeview case sensitively. + * @param model The GtkTreeModel for the treeview. + * @param a The GtkTreeIter for one tree node being compared. + * @param b The GtkTreeIter for the other tree node being compared. + */ +static int pm_sort_iter_compare_func(GtkTreeModel *model, GtkTreeIter *a, + GtkTreeIter *b, gpointer) { + const char *a_text, *b_text; + gtk_tree_model_get(model, a, 1, &a_text, -1); + gtk_tree_model_get(model, b, 1, &b_text, -1); + if (a_text == NULL && b_text == NULL) return 0; + else if (a_text == NULL) return -1; + else if (b_text == NULL) return 1; + else return strcasecmp(a_text, b_text); +} + +// Signals + +/** + * Signal for the activation of the Project Manager entry. + * Requests contents for the treeview. + * @see l_pm_get_contents_for + */ +static void pm_entry_activated(GtkWidget *widget, gpointer) { + const char *entry_text = gtk_entry_get_text(GTK_ENTRY(widget)); + if (l_pm_get_contents_for(entry_text)) l_pm_populate(); +} + +/** + * Signal for a Project Manager keypress. + * Currently handled keypresses: + * - Ctrl+Tab - Refocuses the Scintilla view. + * - Escape - Refocuses the Scintilla view. + */ +static bool pm_keypress(GtkWidget *, GdkEventKey *event, gpointer) { + if (event->keyval == 0xff09 && event->state == GDK_CONTROL_MASK || + event->keyval == 0xff1b) { + gtk_widget_grab_focus(focused_editor); + return true; + } else return false; +} + +/** + * Signal for a Project Manager parent expansion. + * @see pm_open_parent + */ +static void pm_row_expanded(GtkTreeView *, GtkTreeIter *iter, + GtkTreePath *path, gpointer) { + pm_open_parent(iter, path); +} + +/** + * Signal for a Project Manager parent collapse. + * @see pm_close_parent + */ +static void pm_row_collapsed(GtkTreeView *, GtkTreeIter *iter, + GtkTreePath *path, gpointer) { + pm_close_parent(iter, path); +} + +/** + * Signal for the activation of a Project Manager node. + * @see pm_activate_selection + */ +static void pm_row_activated(GtkTreeView *, GtkTreePath *, GtkTreeViewColumn *, + gpointer) { + pm_activate_selection(); +} + +/** + * Signal for a Project Manager mouse click. + * If it is a right-click, popup a context menu for the selected node. + * @see pm_popup_context_menu + */ +static bool pm_button_press(GtkTreeView *, GdkEventButton *event, gpointer) { + if (event->type != GDK_BUTTON_PRESS || event->button != 3) return false; + pm_popup_context_menu(event); return true; +} + +/** + * Signal for popping up a Project Manager context menu. + * Typically Shift+F10 activates this event. + * @see pm_popup_context_menu + */ +static bool pm_popup_menu(GtkWidget *, gpointer) { + pm_popup_context_menu(NULL); return true; +} + +/** + * Signal for a selected Project Manager menu item. + * @see pm_process_selected_menu_item + */ +static void pm_menu_activate(GtkWidget *menu_item, gpointer) { + pm_process_selected_menu_item(menu_item); +} + +// Find/Replace + +#define attach(w, x1, x2, y1, y2, xo, yo, xp, yp) \ + gtk_table_attach(GTK_TABLE(findbox), w, x1, x2, y1, y2, xo, yo, xp, yp) +#define find_text gtk_entry_get_text(GTK_ENTRY(find_entry)) +#define repl_text gtk_entry_get_text(GTK_ENTRY(replace_entry)) +#define toggled(w) gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)) + +/** + * Creates the Find/Replace text frame. + */ +GtkWidget* find_create_ui() { + findbox = gtk_table_new(2, 6, false); + + GtkWidget *flabel = gtk_label_new_with_mnemonic("_Find:"); + GtkWidget *rlabel = gtk_label_new_with_mnemonic("R_eplace:"); + find_entry = gtk_entry_new(); + gtk_widget_set_name(find_entry, "textadept-find-entry"); + replace_entry = gtk_entry_new(); + gtk_widget_set_name(replace_entry, "textadept-replace-entry"); + fnext_button = gtk_button_new_with_mnemonic("Find _Next"); + fprev_button = gtk_button_new_with_mnemonic("Find _Prev"); + r_button = gtk_button_new_with_mnemonic("_Replace"); + ra_button = gtk_button_new_with_mnemonic("Replace _All"); + match_case_opt = gtk_check_button_new_with_mnemonic("_Match case"); + whole_word_opt = gtk_check_button_new_with_mnemonic("_Whole word"); + //incremental_opt = gtk_check_button_new_with_mnemonic("_Incremental"); + lua_opt = gtk_check_button_new_with_mnemonic("_Lua pattern"); + + gtk_label_set_mnemonic_widget(GTK_LABEL(flabel), find_entry); + gtk_label_set_mnemonic_widget(GTK_LABEL(rlabel), replace_entry); + //gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(lua_opt), true); + + attach(find_entry, 1, 2, 0, 1, expand, normal, 5, 0); + attach(replace_entry, 1, 2, 1, 2, expand, normal, 5, 0); + attach(flabel, 0, 1, 0, 1, normal, normal, 5, 0); + attach(rlabel, 0, 1, 1, 2, normal, normal, 5, 0); + attach(fnext_button, 2, 3, 0, 1, normal, normal, 0, 0); + attach(fprev_button, 3, 4, 0, 1, normal, normal, 0, 0); + attach(r_button, 2, 3, 1, 2, normal, normal, 0, 0); + attach(ra_button, 3, 4, 1, 2, normal, normal, 0, 0); + attach(match_case_opt, 4, 5, 0, 1, normal, normal, 5, 0); + attach(whole_word_opt, 4, 5, 1, 2, normal, normal, 5, 0); + //attach(incremental_opt, 5, 6, 0, 1, normal, normal, 5, 0); + attach(lua_opt, 5, 6, 0, 1, normal, normal, 5, 0); + + g_signal_connect(G_OBJECT(find_entry), "key_press_event", + G_CALLBACK(fe_keypress), 0); + g_signal_connect(G_OBJECT(replace_entry), "key_press_event", + G_CALLBACK(re_keypress), 0); + g_signal_connect(G_OBJECT(fnext_button), "clicked", + G_CALLBACK(button_clicked), 0); + g_signal_connect(G_OBJECT(fprev_button), "clicked", + G_CALLBACK(button_clicked), 0); + g_signal_connect(G_OBJECT(r_button), "clicked", + G_CALLBACK(button_clicked), 0); + g_signal_connect(G_OBJECT(ra_button), "clicked", + G_CALLBACK(button_clicked), 0); + + GTK_WIDGET_UNSET_FLAGS(fnext_button, GTK_CAN_FOCUS); + GTK_WIDGET_UNSET_FLAGS(fprev_button, GTK_CAN_FOCUS); + GTK_WIDGET_UNSET_FLAGS(r_button, GTK_CAN_FOCUS); + GTK_WIDGET_UNSET_FLAGS(ra_button, GTK_CAN_FOCUS); + GTK_WIDGET_UNSET_FLAGS(match_case_opt, GTK_CAN_FOCUS); + GTK_WIDGET_UNSET_FLAGS(whole_word_opt, GTK_CAN_FOCUS); + //GTK_WIDGET_UNSET_FLAGS(incremental_opt, GTK_CAN_FOCUS); + GTK_WIDGET_UNSET_FLAGS(lua_opt, GTK_CAN_FOCUS); + + return findbox; +} + +/** + * Toggles the focus between the Find/Replace frame and the current Scintilla + * window. + */ +void find_toggle_focus() { + if (!GTK_WIDGET_HAS_FOCUS(findbox)) { + gtk_widget_show(findbox); + gtk_widget_grab_focus(find_entry); + } else { + gtk_widget_grab_focus(focused_editor); + gtk_widget_hide(findbox); + } +} + +/** + * Builds the integer flags for a Find/Replace depending on the options that are + * checked. + */ +static int get_flags() { + int flags = 0; + if (toggled(match_case_opt)) flags |= SCFIND_MATCHCASE; // 2 + if (toggled(whole_word_opt)) flags |= SCFIND_WHOLEWORD; // 4 + if (toggled(lua_opt)) flags |= 8; + return flags; +} + +/** + * Signal for a Find entry keypress. + * Currently handled keypresses: + * - Enter - Find next or previous. + */ +static bool fe_keypress(GtkWidget *, GdkEventKey *event, gpointer) { + // TODO: if incremental, call l_find() + if (event->keyval == 0xff0d) { + l_find(find_text, get_flags(), true); + return true; + } else return false; +} + +/** + * Signal for a Replace entry keypress. + * Currently handled keypresses: + * - Enter - Find next or previous. + */ +static bool re_keypress(GtkWidget *, GdkEventKey *event, gpointer) { + if (event->keyval == 0xff0d) { + l_find(find_text, get_flags(), true); + return true; + } else return false; +} + +/** + * Signal for a button click. + * Performs the appropriate action depending on the button clicked. + */ +static void button_clicked(GtkWidget *button, gpointer) { + if (button == ra_button) + l_find_replace_all(find_text, repl_text, get_flags()); + else if (button == r_button) { + l_find_replace(repl_text); + l_find(find_text, get_flags(), true); + } else l_find(find_text, get_flags(), button == fnext_button); +} |