#!/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 <cowo@lugbs.linux.it>
# My first python program
# mar ott 30 16:18:46 EST 2001 Giuseppe "Cowo" Corbelli <cowo@lugbs.linux.it>
# 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 <cowo@lugbs.linux.it>
# 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 <cowo@lugbs.linux.it>
# Some cleanup and made a class out of basic global interface.
#dom dic 23 18:37:24 CET 2001 Giuseppe "Cowo" Corbelli <cowo@lugbs.linux.it>
# 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)
|