#!/usr/bin/python # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Library General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # sab ott 27 21:19:16 EDT 2001 Giuseppe "Cowo" Corbelli # My first python program # mar ott 30 16:18:46 EST 2001 Giuseppe "Cowo" Corbelli # Ok, now it works (mostly :-) Still lacking keybindings, but don't forget I'm a newbie :-P #ven nov 02 18:22:57 EST 2001 Giuseppe "Cowo" Corbelli # Some work done with popups and menus. Pipes still don't work reliably. Boh... #mar nov 06 20:28:13 EST 2001 Giuseppe "Cowo" Corbelli # Some cleanup and made a class out of basic global interface. #dom dic 23 18:37:24 CET 2001 Giuseppe "Cowo" Corbelli # Bug fixed when global launched while in directory without GTAGS # Tag search/complete base text search order: # 1: Selected text # 2: Meaningful text before cursor # 3: Text in global entry import os import string import re import glimmer from _gtk import * from gtk import * from GTK import * from GDK import * from gnome.ui import * import gtk import GTK import _gnome import _gnomeui import errno ##################################################################### # Basic no-gui global interface. Pipes and system ##################################################################### class Tglobal_interface: __global_path = None __project_path = None __completionlist = [] def __init__(self, project_path): """ Args: directory containing global indexing files """ for dir in string.split (os.environ['PATH'], ':'): self.__global_path = os.path.join(dir,"global") if os.access(self.__global_path, os.X_OK|os.R_OK): break else: self.__global_path = None if not self.__global_path: raise "GlobalNotFound", "global executable not available in path" if os.path.isdir(project_path) and os.path.exists(project_path): self.__project_path = project_path else: raise "GlobalNoSuchDirectory", "Specified project path ("+project_path+") is invalid" if not os.access(os.path.join(self.__project_path, "GTAGS"), os.R_OK): self.__project_path = None raise "GlobalNoGTAGSFile", "No readable GTAGS file found at path "+project_path def project_path (self): return self.__project_path def getattrib (self, attribname): try: value = self.locals()[attribname] except: return None else: return value def setattrib (self, attribname, value): try: locals()[attribname] except: return None else: self.locals()[attribname] = value return value def update_gtags (self, *args): retcode = os.system (self.__global_path+" -u")>>8 if not retcode == 0: raise "GlobalExecutionError", os.strerror(retcode) def completions (self, initial_string): "Starts global -c 'hint' and parses output. Returns a list of [function name]" self.__completionlist=[] os.chdir (self.__project_path) again = 5 while again: pipe = os.popen(self.__global_path+" -c '"+initial_string+"'", 'r') possible = 'x' while not possible=="": possible = string.strip (pipe.readline()) if not possible=="": self.__completionlist = self.__completionlist + [possible] code = pipe.close() if not code==None: raise "GlobalExecutionError", os.strerror(code) return [] if len(self.__completionlist) == 0: again = again - 1 else: break return self.__completionlist def tags (self, initial_string): "Starts global -x '^hint.*' and parses output. Returns a list of tuples (function call, line, file, details)" self.__completionlist=[] os.chdir (self.__project_path) again = 5 while again: pipe = os.popen(self.__global_path+" -ax '^"+initial_string+".*'", 'r') possible = 'x' while not possible=="": possible = string.strip (pipe.readline()) if not possible=="": reobj = re.search ("^(\w+)\s+(\d+)\s+([\w\/\-\.]+)\s+(.+)$", possible) self.__completionlist.append ( (reobj.group(1), reobj.group(2), reobj.group(3), reobj.group(4)) ) code = pipe.close() if not code==None: raise "GlobalExecutionError", os.strerror(code) return [] if len(self.__completionlist) == 0: again = again - 1 else: break return self.__completionlist def symbols (self, initial_string): "Starts global -gx 'hint' and parses output. Returns a list of tuples (symbol, line, file, details)" self.__completionlist=[] os.chdir (self.__project_path) again = 5 while again: pipe = os.popen(self.__global_path+" -gx '"+initial_string+"'", 'r') possible = 'x' while not possible=="": possible = string.strip (pipe.readline()) if not possible=="": reobj = re.search ("^(\w+)\s+(\d+)\s+([\w\/\-\.]+)\s+(.+)$", possible) if reobj: self.__completionlist.append ( (reobj.group(1), reobj.group(2), reobj.group(3), reobj.group(4)) ) code = pipe.close() if not code==None: raise "GlobalExecutionError", os.strerror(code) return [] if len(self.__completionlist) == 0: again = again - 1 else: break return self.__completionlist ##################################################################### ##################################################################### # Remove script from paned book and all menu entries. Callback for close button. ##################################################################### def global_destroy(button, citem, container): textw = glimmer.get_text_widget (glimmer.get_file_number()) gtk_widget_grab_focus (textw) citem.destroy() _gnomeui.gnome_app_remove_menu_range (glimmer.get_gnome_app(), "_Search/", 11, 1) glimmer.remove_paned_object ("Global") return TRUE ##################################################################### ##################################################################### # Callback for search button. Calls Tglobal_interface instance. ##################################################################### def fetch_data(caller, global_if_inst, symbol_entry, symbols_clist, symbol_togglebutton): "Call Tglobal_interface.symbols or Tglobal_interface.tags and update list" if glimmer.has_selection (): selected = glimmer.get_text (glimmer.selection_start(),glimmer.selection_end ()) selected = string.split(selected).pop() tag = re.search ("(\w+$)", selected).group(1) fromtext = 1 elif re.search ("^\w+.*", symbol_entry.get_text()) == None: #No text defined, find the hint of function to search for selected = glimmer.get_text (glimmer.line_start(),glimmer.current_position ()) selected = string.split(selected).pop() tag = re.search ("(\w+$)", selected).group(1) fromtext = 1 else: tag = string.strip (symbol_entry.get_text ()) if tag == "": return FALSE project_path = global_if_inst.project_path() try: if symbol_togglebutton.get_active(): completionlist=global_if_inst.tags (tag) else: completionlist=global_if_inst.symbols (tag) except "GlobalExecutionError", strerror: GnomeErrorDialog ("Error executing global:\n"+strerror).run() return FALSE else : if len(completionlist) == 0: return FALSE for (functionname, linenumber, filename, details) in completionlist: filename = string.replace (filename, project_path, '') if os.path.isabs(filename): filename = filename[1:] symbols_clist.append ([linenumber, filename, details]) symbols_clist.grab_focus () ##################################################################### ##################################################################### # Callback for tag list selection. Opens a file and searches for selected tag. ##################################################################### def selection_made(caller, rowclicked, columnclicked, event, projpath_entry): "Row clicked in main list" projpath = projpath_entry.get_text() line = caller.get_text (rowclicked, 0) file = caller.get_text (rowclicked, 1) details = caller.get_text (rowclicked, 2) fullfile = os.path.join (projpath, file) if not os.path.exists (fullfile): return for nfile in range(glimmer.get_files()): openfile = glimmer.get_full_filename (nfile) if not os.path.exists (openfile): return if os.path.samefile (fullfile, openfile): #File already opened, go to the right line glimmer.change (nfile) glimmer.move_to_line (int(line)) glimmer.move_to(glimmer.line_start()) textw = glimmer.get_text_widget (nfile) gtk_widget_grab_focus (textw) return #File not opened, do it glimmer.open_file (fullfile) glimmer.move_to_line (int(line)) glimmer.move_to(glimmer.line_start()) textw = glimmer.get_text_widget (glimmer.get_file_number()) gtk_widget_grab_focus (textw) ##################################################################### ##################################################################### # View menu handling ##################################################################### def toggle_global (caller, checkmenuitem, fbox): "Used from View->Global interface" if checkmenuitem.active: fbox.show() else: fbox.hide() return TRUE def hide_global (caller, checkmenuitem, fbox): "Used from Hide button" checkmenuitem.set_active(FALSE) fbox.hide() return TRUE ##################################################################### ##################################################################### # Project path selection routines. Callback for browse button and fileselector buttons. ##################################################################### def browseprojpath_run (caller, projpath_entry): projpath_filesel = GtkFileSelection ("Select project directory") projpath_filesel.set_modal (TRUE) projpath_filesel.show () projpath_filesel.ok_button.connect ("clicked", projpath_selection_ok, projpath_filesel, projpath_entry) projpath_filesel.cancel_button.connect ("clicked", projpath_selection_cancel, projpath_filesel) projpath_filesel.connect ("destroy", projpath_selection_cancel, projpath_filesel) gtk_main () def projpath_selection_ok (caller, filesel, update_entry): "Selected directory/file" dirname = os.path.dirname (filesel.get_filename()) os.chdir (dirname) update_entry.set_text (dirname) filesel.destroy () gtk_main_quit () def projpath_selection_cancel (caller, filesel): "Abort path selection" filesel.destroy () gtk_main_quit () ##################################################################### ##################################################################### # Do not want to insert the proposed completion. Callback for completion list close button. ##################################################################### def complete_selection_cancel (caller, window, symbol_entry): symbol_entry.set_text ('') window.destroy() ##################################################################### ##################################################################### # Insert the proposed completion. Callback for completion list selection. ##################################################################### def complete_selection_made (caller, rowclicked, columnclicked, event, symbol_entry, complete_win, fromtext): "Selected one of the available completions, insert into text if (fromtext)" selected = caller.get_text (rowclicked, 0) if fromtext == 0: symbol_entry.set_text (selected) complete_win.destroy () return TRUE else: hint = symbol_entry.get_text () print hint #Erase the hint we got glimmer.delete_text (glimmer.current_position()-len(hint), glimmer.current_position()) glimmer.insert (selected) symbol_entry.set_text ('') complete_win.destroy () return TRUE ##################################################################### ##################################################################### # Popup a completion list. Callback for complete button. ##################################################################### def search_winpopup (caller, global_if_inst, symbol_entry): "calls Tglobal_interface.completions to retrieve list of functions, displays them in a popup" fromtext = 0 if glimmer.has_selection (): selected = glimmer.get_text (glimmer.selection_start(),glimmer.selection_end ()) selected = string.split(selected).pop() function = re.search ("(\w+$)", selected).group(1) fromtext = 1 elif re.search ("^\w+.*", symbol_entry.get_text()) == None: #No text defined, find the hint of function to search for selected = glimmer.get_text (glimmer.line_start(),glimmer.current_position ()) selected = string.split(selected).pop() function = re.search ("(\w+$)", selected).group(1) fromtext = 1 else: function = string.strip (symbol_entry.get_text ()) if function == "": return FALSE symbol_entry.set_text (function) try: completionlist=global_if_inst.completions (symbol_entry.get_text()) except "GlobalExecutionError", strerror: GnomeErrorDialog ("Error executing global:\n"+strerror).run() return FALSE if len(completionlist)==0: if fromtext: symbol_entry.set_text ('') return FALSE complete_win = GtkWindow (WINDOW_POPUP) complete_win.set_usize (150, 150) cwin_vbox = GtkVBox (FALSE, 2) complete_clist = GtkCList (1) close_button = GtkButton ("Close") accel_group = GtkAccelGroup () complete_win.set_position (WIN_POS_MOUSE) complete_scrolledwin = GtkScrolledWindow (None, None) complete_scrolledwin.set_policy (POLICY_AUTOMATIC, POLICY_AUTOMATIC) complete_scrolledwin.set_border_width (0) complete_clist.set_column_width (0, 250) complete_clist.clear () complete_clist.set_shadow_type (SHADOW_NONE) for stringa in completionlist: complete_clist.append ([stringa]) complete_win.set_border_width (2) complete_win.add (cwin_vbox) complete_scrolledwin.add (complete_clist) cwin_vbox.pack_start (complete_scrolledwin) cwin_vbox.pack_end (close_button, FALSE, FALSE, 0) close_button.add_accelerator ("clicked", accel_group, C, MOD1_MASK, ACCEL_VISIBLE) close_button.add_accelerator ("clicked", accel_group, Escape, 0, ACCEL_VISIBLE) complete_win.add_accel_group (accel_group) close_button.connect ("clicked", complete_selection_cancel, complete_win, symbol_entry) complete_clist.connect ("select_row", complete_selection_made, symbol_entry, complete_win, fromtext) complete_win.foreach (showall) complete_win.show () complete_win.set_modal (TRUE) complete_clist.grab_focus () ##################################################################### ##################################################################### # Recursively shows all childs of a widget. I didn't know of gtk_widget_show_all :-)) ##################################################################### def showall (widget): widget.show() if GTK_CHECK_TYPE(widget._o, GtkContainer.get_type()): widget.foreach(showall) ##################################################################### ##################################################################### # Start of the main code ##################################################################### total_vbox = GtkVBox (FALSE, 2) header_hbox = GtkHBox (FALSE, 2) global_label = GtkLabel ("Global interface\nby Giuseppe \"Cowo\" Corbelli") close_button = GtkButton ("Close") close_button.set_relief(RELIEF_HALF) hide_button = GtkButton ("Hide") hide_button.set_relief(RELIEF_HALF) separator = GtkHSeparator() symbol_frame = GtkFrame ("Symbol/Function") symbol_vbox = GtkVBox (TRUE, 1) symbol_hbox = GtkHBox (FALSE, 2) symbol_entry = GtkEntry() if glimmer.has_selection(): symbol_entry.set_text (glimmer.get_text(glimmer.selection_start(), glimmer.selection_end())) symbol_togglebutton = GtkToggleButton ("Funcs only") search_button = GtkButton ("Search") search_button.set_relief (RELIEF_HALF) complete_button = GtkButton ("Complete") complete_button.set_relief (RELIEF_HALF) projpath_frame = GtkFrame ("Project path") projpath_vbox = GtkVBox (TRUE, 1) projpath_hbox = GtkHBox (FALSE, 2) projpath_entry = GtkEntry() updateglobal_button = GtkButton ("Update GTAGS") browseprojpath_button = GtkButton ("Browse") browseprojpath_button.set_relief (RELIEF_HALF) clearlist_button = GtkButton ("Clear List") clearlist_button.set_relief (RELIEF_HALF) symbols_scrolledwin = GtkScrolledWindow (None, None) symbols_scrolledwin.set_policy (POLICY_ALWAYS, POLICY_ALWAYS) symbols_clist = GtkCList (3, ["Line", "File", "Details"]) symbols_clist.set_selection_mode (SELECTION_SINGLE) symbols_clist.set_column_width (1, 100) symbols_clist.set_column_width (2, 500) symbols_clist.column_title_passive (0) symbols_clist.column_title_passive (1) symbols_clist.column_title_passive (2) globaltoggle_checkitem = GtkCheckMenuItem("Global interface") globaltoggle_checkitem.set_active(TRUE) ##################################################################### # Customizing View and Search menus ##################################################################### globaltoggle_checkitem.show() glimmer.add_widget_to_menu("_View/Status Bar", globaltoggle_checkitem._o) #Add global menu section glimmer.add_sub_to_menu ("_Search/", "Use global", 12) glimmer.add_item_to_menu ("_Search/Use global/", "Complete", "Try to complete text using global", '', 1, complete_button.clicked) glimmer.add_item_to_menu ("_Search/Use global/", "Search tag/symbol", "Search tag/symbol using global", '', 2, search_button.clicked) ##################################################################### ##################################################################### #Packing section ##################################################################### total_vbox.pack_start (header_hbox, FALSE, FALSE, 2) header_hbox.pack_start (close_button, FALSE, FALSE, 2) header_hbox.pack_start (global_label, TRUE, TRUE, 2) header_hbox.pack_start (hide_button, FALSE, FALSE, 2) total_vbox.pack_start (separator, FALSE, FALSE, 1) total_vbox.pack_start (symbol_frame, FALSE, FALSE, 2) symbol_frame.add (symbol_vbox) symbol_vbox.pack_start (symbol_entry, TRUE, TRUE, 2) symbol_vbox.pack_end (symbol_hbox, TRUE, TRUE, 2) symbol_hbox.pack_start (symbol_togglebutton, TRUE, FALSE, 2) symbol_hbox.pack_start (complete_button, TRUE, FALSE, 2) symbol_hbox.pack_start (search_button, TRUE, FALSE, 2) total_vbox.pack_start (projpath_frame, FALSE, FALSE, 2) projpath_frame.add (projpath_vbox) projpath_vbox.pack_start (projpath_entry, TRUE, TRUE, 2) projpath_vbox.pack_end (projpath_hbox, TRUE, TRUE, 2) projpath_hbox.pack_start (browseprojpath_button, TRUE, FALSE, 2) projpath_hbox.pack_end (updateglobal_button, TRUE, FALSE, 2) projpath_hbox.pack_end (clearlist_button, TRUE, FALSE, 2) total_vbox.pack_start (symbols_scrolledwin, TRUE, TRUE, 2) symbols_scrolledwin.add (symbols_clist) gtk_widget_show_all (total_vbox._o) glimmer.add_paned_object(total_vbox._o, "Global", 0) ##################################################################### browseprojpath_button.connect ("clicked", browseprojpath_run, projpath_entry) ##################################################################### # Must have a valid path before activating gui components ##################################################################### global_interface = None while global_interface == None: try: global_interface = Tglobal_interface (os.path.join(os.getcwd())) except "GlobalNotFound", strerror: GnomeErrorDialog ("ERROR!!!\n"+strerror+"\n"+os.environ["PATH"]).run() close_button.clicked() except ("GlobalNoSuchDirectory", "GlobalNoGTAGSFile"), strerror: GnomeErrorDialog ("ERROR!!!\n"+strerror).run() symbol_entry.set_sensitive (FALSE) symbol_togglebutton.set_sensitive (FALSE) search_button.set_sensitive (FALSE) complete_button.set_sensitive (FALSE) updateglobal_button.set_sensitive (FALSE) symbols_scrolledwin.set_sensitive (FALSE) browseprojpath_button.clicked () else: symbol_entry.set_sensitive (TRUE) symbol_togglebutton.set_sensitive (TRUE) search_button.set_sensitive (TRUE) complete_button.set_sensitive (TRUE) updateglobal_button.set_sensitive (TRUE) symbols_scrolledwin.set_sensitive (TRUE) projpath_entry.set_text (os.path.join(os.getcwd())) ##################################################################### symbol_entry.grab_focus () #Signals total_vbox.connect ("destroy", global_destroy, globaltoggle_checkitem, total_vbox) close_button.connect ("clicked", global_destroy, globaltoggle_checkitem, total_vbox) hide_button.connect ("clicked", hide_global, globaltoggle_checkitem, total_vbox) globaltoggle_checkitem.connect ("toggled", toggle_global, globaltoggle_checkitem, total_vbox) complete_button.connect ("clicked", search_winpopup, global_interface, symbol_entry) search_button.connect ("clicked", fetch_data, global_interface, symbol_entry, symbols_clist, symbol_togglebutton) symbol_entry.connect ("activate", fetch_data, projpath_entry, symbol_entry, symbols_clist, symbol_togglebutton) symbols_clist.connect ("select_row", selection_made, projpath_entry) clearlist_button.connect ("clicked", symbols_clist.clear) updateglobal_button.connect ("clicked", global_interface.update_gtags)