diff --git a/help/prefs.html b/help/prefs.html
index 06baaf2..793bfc3 100644
--- a/help/prefs.html
+++ b/help/prefs.html
@@ -77,12 +77,8 @@
Kodos - Help
Preferences
-Set your Web Browser path. You can use the ... button to use a file dialog to help locate your browser. The web browser is necessary to access external links within the Python Regex documentation.
-
The Editor Font allows you to select a desirable font for use within the Kodos editor
-The Email Server is required for sending bug reports to the author. You can submit a bug report under the main Help menu
-
You can control the number of Recent Files that are displayed at the bottom of the File menu. The default value is 5. To display more or less recent files, set this value accordingly. The recent file list is maintained independtly of this value such that applying a lower value and then a higher value will not purge entries. That is, if you have 10 files and then set this value to 3 and later set this value to 10, the 10 files will be immediately available for selection .
diff --git a/kodos b/kodos
index 8446047..b742888 100755
--- a/kodos
+++ b/kodos
@@ -4,1055 +4,21 @@
import sys
import os
-import string
-import re
-import cPickle
-import types
import getopt
-import urllib
-import signal
try:
- from PyQt4.QtGui import *
- from PyQt4.QtCore import *
+ from PyQt4 import QtGui
+ from PyQt4 import QtCore
except:
sys.exit("""Could not locate the PyQt module. Please make sure that
you have installed PyQt for the version of Python that you are running.""")
### make sure that this script can find kodos specific modules ###
from distutils.sysconfig import get_python_lib
-
sys.path.append(os.path.join(get_python_lib(), "kodos"))
-###################################################################
-
-from modules.kodosBA import *
-from modules.util import *
-from modules.about import *
-import modules.help as help
-from modules.status_bar import *
-from modules.reference import *
-from modules.prefs import *
-from modules.reportBug import reportBugWindow
-from modules.version import VERSION
-from modules.recent_files import RecentFiles
-from modules.urlDialog import URLDialog
-from modules.regexLibrary import RegexLibrary
-from modules.newUserDialogBA import NewUserDialog
-from modules.flags import reFlag, reFlagList
-
-
-# match status
-MATCH_NA = 0
-MATCH_OK = 1
-MATCH_FAIL = 2
-MATCH_PAUSED = 3
-MATCH_EXAMINED = 4
-
-TRUE = 1
-FALSE = 0
-
-TIMEOUT = 3
-
-# regex to find special flags which must begin at beginning of line
-# or after some spaces
-EMBEDDED_FLAGS = r"^ *\(\?(?P[iLmsux]*)\)"
-
-RX_BACKREF = re.compile(r"""\\\d""")
-
-STATE_UNEDITED = 0
-STATE_EDITED = 1
-
-GEO = "kodos_geometry"
-
-# colors for normal & examination mode
-QCOLOR_WHITE = QColor(Qt.white) # normal
-QCOLOR_YELLOW = QColor(255,255,127) # examine
-
-try:
- signal.SIGALRM
- HAS_ALARM = 1
-except:
- HAS_ALARM = 0
-
-
-##############################################################################
-#
-# The Kodos class which defines the main functionality and user interaction
-#
-##############################################################################
-
-class Kodos(KodosBA):
- def __init__(self, filename, debug):
- KodosBA.__init__(self)
-
- self.debug = debug
- self.regex = ""
- self.matchstring = ""
- self.replace = ""
- self.is_paused = 0
- self.is_examined = 0
- self.filename = ""
- self.match_num = 1 # matches are labeled 1..n
- self.replace_num = 0 # replace all
- self.url = None
- self.group_tuples = None
- self.editstate = STATE_UNEDITED
-
- self.ref_win = None
- self.regexlibwin = None
-
- self.embedded_flags_obj = re.compile(EMBEDDED_FLAGS)
- self.regex_embedded_flags_removed = ""
-
- self.createStatusBar()
-
- self.MSG_NA = self.tr("Enter a regular expression and a string to match against")
- self.MSG_PAUSED = self.tr("Kodos regex processing is paused. Click the pause icon to unpause")
- self.MSG_FAIL = self.tr("Pattern does not match")
-
-
- self.statusPixmapsDict = { MATCH_NA: QPixmap(":images/yellow.png"),
- MATCH_OK: QPixmap(":images/green.png"),
- MATCH_FAIL: QPixmap(":images/red.png"),
- MATCH_PAUSED: QPixmap(":images/pause.png"),
- }
-
-
- self.updateStatus(self.MSG_NA, MATCH_NA)
-
- self.reFlags = reFlagList([
- reFlag("re.IGNORECASE", "i", self.ignorecaseCheckBox),
- reFlag("re.MULTILINE", "m", self.multilineCheckBox),
- reFlag("re.DOTALL", "s", self.dotallCheckBox),
- reFlag("re.VERBOSE", "x", self.verboseCheckBox),
- reFlag("re.LOCALE", "L", self.localeCheckBox),
- reFlag("re.UNICODE", "u", self.unicodeCheckBox),
- ])
- self.reFlags.clearAll()
-
- restoreWindowSettings(self, GEO)
-
- self.show()
-
- self.prefs = Preferences(self, 1)
- self.recent_files = RecentFiles(self,
- self.prefs.recentFilesSpinBox.value(),
- self.debug)
-
- if filename and self.openFile(filename):
- qApp.processEvents()
-
- self.fileMenu.triggered.connect(self.fileMenuHandler)
-
- kodos_toolbar_logo(self.toolBar)
- if self.replace: self.show_replace_widgets()
- else: self.hide_replace_widgets()
-
- self.checkIfNewUser()
-
-
- def checkIfNewUser(self):
- s = QSettings()
- if s.value('New User', "true").toPyObject() != "false":
- self.newuserdialog = NewUserDialog()
- self.newuserdialog.show()
- s.setValue('New User', "false")
-
-
- def createStatusBar(self):
- self.status_bar = Status_Bar(self, FALSE, "")
-
-
- def updateStatus(self, status_string, status_value, duration=0, replace=FALSE, tooltip=''):
- pixmap = self.statusPixmapsDict.get(status_value)
-
- self.status_bar.set_message(status_string, duration, replace, tooltip, pixmap)
-
-
- def fileMenuHandler(self, menuid):
- if self.recent_files.isRecentFile(menuid):
- fn = str(menuid.text())
- if self.openFile(fn):
- self.recent_files.add(fn)
-
- def prefsSaved(self):
- if self.debug: print "prefsSaved slot"
- self.recent_files.setNumShown(self.prefs.recentFilesSpinBox.value())
-
-
- def kodos_edited_slot(self):
- # invoked whenever the user has edited something
- self.editstate = STATE_EDITED
-
-
- def checkbox_slot(self):
- self.process_regex()
-
-
- def set_flags(self, flags):
- # from the given integer value of flags, set the checkboxes
- # this is used when loading a saved file
- for f in self.reFlags:
- f.checkBox.setChecked(flags & f.reFlag)
-
-
- def get_flags_string(self):
- flags_str = ""
-
- for f in self.reFlags:
- if f.checkBox.isChecked():
- flags_str += "| " + f.flagName
-
- if flags_str:
- flags_str = ", " + flags_str[1:]
- return flags_str
-
-
- def get_embedded_flags_string(self):
- flags_str = flags = ""
-
- for f in self.reFlags:
- if f.checkBox.isChecked():
- flags += f.shortFlag
-
- if flags:
- flags_str = "(?" + flags + ")"
-
- return flags_str
-
-
- def pause(self):
- self.is_paused = not self.is_paused
- if self.debug: print "is_paused:", self.is_paused
-
- if self.is_paused:
- self.update_results(self.MSG_PAUSED, MATCH_PAUSED)
- self.matchNumberSpinBox.setDisabled(1)
-
- else:
- self.process_regex()
- self.matchNumberSpinBox.setEnabled(1)
-
-
- def examine(self):
- self.is_examined = not self.is_examined
- if self.debug: print "is_examined:", self.is_examined
-
- if self.is_examined:
- color = QCOLOR_YELLOW
- regex = self.regex
- self.regex_saved = self.regex
- length = len(regex)
- self.regexMultiLineEdit.setReadOnly(1)
- self.stringMultiLineEdit.setReadOnly(1)
- self.replaceTextEdit.setReadOnly(1)
- for i in range(length, 0, -1):
- regex = regex[:i]
- self.process_embedded_flags(self.regex)
- try:
- m = re.search(regex, self.matchstring, self.reFlags.allFlagsORed())
- if m:
- if self.debug: print "examined regex:", regex
- self.__refresh_regex_widget(color, regex)
- return
- except:
- pass
-
- self.__refresh_regex_widget(color, "")
- else:
- regex = self.regex_saved
- color = QCOLOR_WHITE
- self.regexMultiLineEdit.setReadOnly(0)
- self.stringMultiLineEdit.setReadOnly(0)
- self.replaceTextEdit.setReadOnly(0)
- self.__refresh_regex_widget(color, regex)
-
-
- def __refresh_regex_widget(self, base_qcolor, regex):
- pal = QPalette()
- pal.setColor(pal.Base, base_qcolor)
- self.regexMultiLineEdit.setPalette(pal)
-
- self.regexMultiLineEdit.blockSignals(1)
- self.regexMultiLineEdit.clear()
- self.regexMultiLineEdit.blockSignals(0)
- self.regexMultiLineEdit.setPlainText(regex)
-
-
- def match_num_slot(self, num):
- self.match_num = num
- self.process_regex()
-
-
- def replace_num_slot(self, num):
- self.replace_num = num
- self.process_regex()
-
-
- def regex_changed_slot(self):
- self.regex = unicode(self.regexMultiLineEdit.toPlainText())
- self.process_regex()
-
-
- def string_changed_slot(self):
- self.matchstring = unicode(self.stringMultiLineEdit.toPlainText())
- self.process_regex()
-
- def helpContents(self, x = None):
- pass #FIXME
- def helpIndex(self, x = None):
- pass #FIXME
-
- def hide_replace_widgets(self):
- self.spacerLabel.hide()
- self.replaceLabel.hide()
- self.replaceNumberSpinBox.hide()
- self.replaceTextBrowser.clear()
- self.replaceTextBrowser.setDisabled(TRUE)
-
- def show_replace_widgets(self):
- self.spacerLabel.show()
- self.replaceLabel.show()
- self.replaceNumberSpinBox.show()
- self.replaceNumberSpinBox.setEnabled(TRUE)
- self.replaceTextBrowser.setEnabled(TRUE)
-
- def replace_changed_slot(self):
- self.replace = unicode(self.replaceTextEdit.toPlainText())
- self.process_regex()
- if not self.replace:
- self.hide_replace_widgets()
- else:
- self.show_replace_widgets()
-
-
- def update_results(self, msg, val):
- self.updateStatus(msg, val)
-
-
- def populate_group_table(self, tuples):
- rows = len(tuples)
- # Remove old rows for groups that no longer exist
- for i in range(rows, self.groupTable.rowCount()):
- self.groupTable.removeRow(i)
-
- self.groupTable.setRowCount(rows)
- row = 0
- for t in tuples:
- self.groupTable.setItem(row, 0, QTableWidgetItem(t[1]))
- self.groupTable.setItem(row, 1, QTableWidgetItem(t[2]))
- row += 1
-
-
- def populate_code_textbrowser(self):
- self.codeTextBrowser.clear()
-
- code = "import re\n\n"
- code += "# common variables\n\n"
- code += "rawstr = r\"\"\"" + self.regex_embedded_flags_removed + "\"\"\"\n"
- code += "embedded_rawstr = r\"\"\"" + self.get_embedded_flags_string() + \
- self.regex_embedded_flags_removed + "\"\"\"\n"
- code += 'matchstr = \"\"\"' + self.matchstring + '\"\"\"'
- code += "\n\n"
- code += "# method 1: using a compile object\n"
- code += "compile_obj = re.compile(rawstr"
- code += self.get_flags_string()
- code += ")\n"
- code += "match_obj = compile_obj.search(matchstr)\n\n"
-
- code += "# method 2: using search function (w/ external flags)\n"
- code += "match_obj = re.search(rawstr, matchstr"
- code += self.get_flags_string()
- code += ")\n\n"
-
- code += "# method 3: using search function (w/ embedded flags)\n"
- code += "match_obj = re.search(embedded_rawstr, matchstr)\n\n"
-
-
- if self.group_tuples:
- code += "# Retrieve group(s) from match_obj\n"
- code += "all_groups = match_obj.groups()\n\n"
- code += "# Retrieve group(s) by index\n"
- i = 0
- named_grps = 0
- for grp in self.group_tuples:
- i += 1
- code += "group_%d = match_obj.group(%d)\n" % (i, i)
- if grp[1]: named_grps = 1
-
- if named_grps:
- code += "\n# Retrieve group(s) by name\n"
- for grp in self.group_tuples:
- if grp[1]:
- code += "%s = match_obj.group('%s')\n" % (grp[1], grp[1])
-
- code += "\n"
-
- if self.replace:
- code += "# Replace string\n"
- code += "newstr = compile_obj.subn('%s', %d)\n" % (self.replace,
- self.replace_num)
-
- self.codeTextBrowser.setPlainText(code)
-
-
- def colorize_strings(self, strings, widget, cursorOffset=0):
- widget.clear()
-
- colors = (QBrush(QColor(Qt.black)), QBrush(QColor(Qt.blue)) )
- cur = widget.textCursor()
- format = cur.charFormat()
-
- pos = cur.position()
- i = 0
- for s in strings:
- format.setForeground(colors[i%2])
- cur.insertText(s, format)
- if i == cursorOffset:
- pos = cur.position()
- i += 1
-
- cur.setPosition(pos)
- widget.setTextCursor(cur)
- widget.centerCursor()
-
-
- def populate_match_textbrowser(self, startpos, endpos):
- pre = post = match = ""
-
- match = self.matchstring[startpos:endpos]
-
- # prepend the beginning that didn't match
- if startpos > 0:
- pre = self.matchstring[0:startpos]
-
- # append the end that didn't match
- if endpos < len(self.matchstring):
- post = self.matchstring[endpos:]
-
- strings = [pre, match, post]
- self.colorize_strings(strings, self.matchTextBrowser, 1)
-
-
- def populate_replace_textbrowser(self, spans, nummatches, compile_obj):
- self.replaceTextBrowser.clear()
- if not spans: return
-
- num = self.replaceNumberSpinBox.value()
- if num == 0: num = nummatches
- text = self.matchstring
-
- replace_text = unicode(self.replaceTextEdit.toPlainText())
- if RX_BACKREF.search(replace_text):
- # if the replace string contains a backref we just use the
- # python regex methods for the substitution
- replaced = compile_obj.subn(replace_text, text, num)[0]
- self.replaceTextBrowser.setPlainText(replaced)
- return
-
- numreplaced = idx = 0
-
- strings = []
-
- for span in spans:
- if span[0] != 0:
- s = text[idx:span[0]]
- else:
- s = ""
-
- idx = span[1]
- numreplaced += 1
-
- strings.append(s)
- strings.append(self.replace)
-
- if numreplaced >= num:
- strings.append(text[span[1]:])
- break
-
- self.colorize_strings(strings, self.replaceTextBrowser)
-
-
- def populate_matchAll_textbrowser(self, spans):
- self.matchAllTextBrowser.clear()
- if not spans: return
-
- idx = 0
- text = self.matchstring
- strings = []
- for span in spans:
- if span[0] != 0:
- s = text[idx:span[0]]
- else:
- s = ""
-
- idx = span[1]
- strings.append(s)
- strings.append(text[span[0]:span[1]])
-
- if 0 <= idx <= len(text):
- strings.append(text[span[1]:])
-
- self.colorize_strings(strings, self.matchAllTextBrowser)
-
-
- def clear_results(self):
- # .clear() destroys the headers, and .clearContents() doesn't do
- # anything at all, so remove the rows one by one
- for i in range(self.groupTable.rowCount()):
- self.groupTable.removeRow(i)
-
- self.codeTextBrowser.clear()
- self.matchTextBrowser.clear()
- self.matchNumberSpinBox.setEnabled(FALSE)
- self.replaceNumberSpinBox.setEnabled(FALSE)
- self.replaceTextBrowser.clear()
- self.matchAllTextBrowser.clear()
-
-
- def process_regex(self):
- def timeout(signum, frame):
- return
-
- if self.is_paused:
- return
-
- self.process_embedded_flags(self.regex)
-
- if not self.regex or not self.matchstring:
- self.update_results(self.MSG_NA, MATCH_NA)
- self.clear_results()
- return
-
- if HAS_ALARM:
- signal.signal(signal.SIGALRM, timeout)
- signal.alarm(TIMEOUT)
-
- try:
- compile_obj = re.compile(self.regex, self.reFlags.allFlagsORed())
- allmatches = compile_obj.findall(self.matchstring)
-
- if allmatches and len(allmatches):
- self.matchNumberSpinBox.setMaximum(len(allmatches))
- self.matchNumberSpinBox.setEnabled(TRUE)
- self.replaceNumberSpinBox.setMaximum(len(allmatches))
- self.replaceNumberSpinBox.setEnabled(TRUE)
- else:
- self.matchNumberSpinBox.setEnabled(FALSE)
- self.replaceNumberSpinBox.setEnabled(FALSE)
-
- match_obj = compile_obj.search(self.matchstring)
-
- except Exception, e:
- self.update_results(unicode(e), MATCH_FAIL)
- return
-
- if HAS_ALARM:
- signal.alarm(0)
-
- if not match_obj:
- self.update_results(self.MSG_FAIL, MATCH_FAIL)
-
- self.clear_results()
- return
-
- # match_index is the list element for match_num.
- # Therefor match_num is for ui display
- # and match_index is for application logic.
- match_index = self.match_num - 1
-
- if match_index > 0:
- for i in range(match_index):
- match_obj = compile_obj.search(self.matchstring,
- match_obj.end())
-
- self.populate_match_textbrowser(match_obj.start(), match_obj.end())
-
- self.group_tuples = []
-
- if match_obj.groups():
- group_nums = {}
- if compile_obj.groupindex:
- keys = compile_obj.groupindex.keys()
- for key in keys:
- group_nums[compile_obj.groupindex[key]] = key
-
- if self.debug:
- print "group_nums:", group_nums
- print "grp index: ", compile_obj.groupindex
- print "groups:", match_obj.groups()
- print "span: ", match_obj.span()
-
- # create group_tuple in the form: (group #, group name, group matches)
- g = allmatches[match_index]
- if type(g) == types.TupleType:
- for i in range(len(g)):
- group_tuple = (i+1, group_nums.get(i+1, ""), g[i])
- self.group_tuples.append(group_tuple)
- else:
- self.group_tuples.append( (1, group_nums.get(1, ""), g) )
-
- self.populate_group_table(self.group_tuples)
- else:
- # clear the group table
- self.populate_group_table([])
-
- str_pattern_matches = unicode(self.tr("Pattern matches"))
- str_found = unicode(self.tr("found"))
- str_match = unicode(self.tr("match"))
- str_matches = unicode(self.tr("matches"))
-
- if len(allmatches) == 1:
- status = "%s (%s 1 %s)" % (str_pattern_matches,
- str_found,
- str_match)
- else:
- status = "%s (%s %d %s)" % (str_pattern_matches,
- str_found,
- len(allmatches),
- str_matches)
-
- self.update_results(status, MATCH_OK)
- self.populate_code_textbrowser()
-
- spans = self.findAllSpans(compile_obj)
- if self.replace:
- self.populate_replace_textbrowser(spans, len(allmatches), compile_obj)
- self.populate_matchAll_textbrowser(spans)
-
-
- def findAllSpans(self, compile_obj):
- spans = []
-
- match_obj = compile_obj.search(self.matchstring)
-
- last_span = None
-
- while match_obj:
- start = match_obj.start()
- end = match_obj.end()
- span = (start, end)
- if last_span == span: break
-
- spans.append(span)
-
- last_span = span
- match_obj = compile_obj.search(self.matchstring, end)
-
- return spans
-
-
- def closeEvent(self, ev):
- if not self.checkEditState():
- ev.ignore()
- return
-
- saveWindowSettings(self, GEO)
-
- try:
- self.regexlibwin.close()
- except:
- pass
-
- try:
- self.ref_win.close()
- except:
- pass
- ev.accept()
-
-
- def fileNew(self):
- if not self.checkEditState():
- return
- self.filename = ""
-
- self.regexMultiLineEdit.setPlainText("")
- self.stringMultiLineEdit.setPlainText("")
- self.replaceTextEdit.setPlainText("")
- self.set_flags(0)
- self.editstate = STATE_UNEDITED
-
-
- def importURL(self):
- self.urldialog = URLDialog(self, self.url)
- self.urldialog.urlImported.connect(self.urlImported)
-
-
- def urlImported(self, html, url):
- self.url = url
- self.stringMultiLineEdit.setPlainText(html)
-
-
- def importFile(self):
- fn = QFileDialog.getOpenFileName(self,
- self.tr("Import File"),
- self.filename,
- self.tr("All (*)"))
-
- if fn.isEmpty():
- self.updateStatus(self.tr("A file was not selected for import"),
- -1,
- 5,
- TRUE)
- return None
-
- filename = str(fn)
-
- try:
- fp = open(filename, "r")
- except:
- msg = self.tr("Could not open file for reading: ") + filename
- self.updateStatus(msg, -1, 5, TRUE)
- return None
-
- data = fp.read()
- fp.close()
- self.stringMultiLineEdit.setPlainText(data)
-
-
- def fileOpen(self):
- filename = self.filename
- if filename == None:
- filename = ""
- fn = QFileDialog.getOpenFileName(self,
- self.tr("Open Kodos File"),
- filename,
- self.tr("Kodos file (*.kds);;All (*)"))
- if not fn.isEmpty():
- filename = str(fn)
- if self.openFile(filename):
- self.recent_files.add(filename)
-
-
- def openFile(self, filename):
- if not self.checkEditState():
- return
-
- self.filename = None
-
- try:
- fp = open(filename, "r")
- except:
- msg = self.tr("Could not open file for reading: ") + filename
- self.updateStatus(msg, -1, 5, TRUE)
- return None
-
- try:
- u = cPickle.Unpickler(fp)
- self.regex = u.load()
- self.matchstring = u.load()
- flags = u.load()
- except Exception, e: #FIXME: don't catch everything
- if self.debug:
- print unicode(e)
- msg = "%s %s" % (unicode(self.tr("Error reading from file:")),
- filename)
- self.updateStatus(msg, -1, 5, TRUE)
- return 0
-
- self.matchNumberSpinBox.setValue(1)
- self.regexMultiLineEdit.setPlainText(self.regex)
- self.stringMultiLineEdit.setPlainText(self.matchstring)
-
- self.set_flags(flags)
-
- try:
- replace = u.load()
- except:
- # versions prior to 1.7 did not have replace functionality
- # so kds files saved w/ these versions will throw exception
- # here.
- replace = ""
- self.replaceTextEdit.setPlainText(replace)
-
- self.filename = filename
- msg = "%s %s" % (filename, unicode(self.tr("loaded successfully")))
- self.updateStatus(msg, -1, 5, TRUE)
- self.editstate = STATE_UNEDITED
- return 1
-
-
- def fileSaveAs(self):
- filename = self.filename
- if filename == None:
- filename = ""
- filedialog = QFileDialog(self,
- self.tr("Save Kodos File"),
- filename,
- "Kodos file (*.kds);;All (*)")
- filedialog.setAcceptMode(QFileDialog.AcceptSave)
- filedialog.setDefaultSuffix("kds")
- ok = filedialog.exec_()
-
- if ok == QDialog.Rejected:
- self.updateStatus(self.tr("No file selected to save"), -1, 5, TRUE)
- return
-
- filename = os.path.normcase(unicode(filedialog.selectedFiles().first()))
-
- self.filename = filename
- self.fileSave()
-
-
- def fileSave(self):
- if not self.filename:
- self.fileSaveAs()
- return
-
- try:
- fp = open(self.filename, "w")
- except:
- msg = "%s: %s" % (unicode(self.tr("Could not open file for writing:")),
- self.filename)
- self.updateStatus(msg, -1, 5, TRUE)
- return None
-
- self.editstate = STATE_UNEDITED
- p = cPickle.Pickler(fp)
- p.dump(self.regex)
- p.dump(self.matchstring)
- p.dump(self.reFlags.allFlagsORed())
- p.dump(self.replace)
-
- fp.close()
- msg = "%s %s" % (unicode(self.filename),
- unicode(self.tr("successfully saved")))
- self.updateStatus(msg, -1, 5, TRUE)
- self.recent_files.add(self.filename)
-
-
- def paste_symbol(self, symbol):
- self.regexMultiLineEdit.insertPlainText(symbol)
-
-
- def process_embedded_flags(self, regex):
- # determine if the regex contains embedded regex flags.
- # if it does, set the appropriate checkboxes on the UI to reflect the flags that are embedded
- match = self.embedded_flags_obj.match(regex)
- if not match:
- embedded_flags = ""
- self.regex_embedded_flags_removed = regex
- else:
- embedded_flags = match.group('flags')
- self.regex_embedded_flags_removed = self.embedded_flags_obj.sub("", regex, 1)
-
- for f in self.reFlags:
- if f.shortFlag in embedded_flags:
- f.embed()
- else:
- f.deembed()
-
-
- def checkEditState(self):
- if self.editstate == STATE_EDITED:
- message = self.tr("You have made changes. Would you like to save them before continuing?")
-
- prompt = QMessageBox.warning(None,
- self.tr("Save changes?"),
- message,
- QMessageBox.Save |
- QMessageBox.Cancel |
- QMessageBox.Discard)
-
- if prompt == QMessageBox.Cancel:
- return False
-
- if prompt == QMessageBox.Save:
- self.fileSave()
- if not self.filename: self.checkEditState()
-
- return True
-
-
- def pasteFromRegexLib(self, d):
- if not self.checkEditState():
- return
-
- self.filename = ""
-
- self.regexMultiLineEdit.setPlainText(d.get('regex', ""))
- self.stringMultiLineEdit.setPlainText(d.get('text', ""))
- self.replaceTextEdit.setPlainText(d.get('replace', ""))
-
- try:
- # set the current page if applicable
- self.resultTabWidget.setCurrentIndex(int(d['tab']))
- except KeyError:
- pass
- self.editstate = STATE_UNEDITED
-
-
- def revert_file_slot(self):
- if not self.filename:
- self.updateStatus(self.tr("There is no filename to revert"),
- -1,
- 5,
- TRUE)
- return
-
- self.openFile(self.filename)
-
-
- def getWidget(self):
- widget = qApp.focusWidget()
- if (widget == self.regexMultiLineEdit or
- widget == self.stringMultiLineEdit or
- widget == self.replaceTextEdit or
- widget == self.codeTextBrowser):
- return widget
- else:
- return None
-
-
- def widgetMethod(self, methodstr, anywidget=0):
- # execute the methodstr of widget only if widget
- # is one of the editable widgets OR if the method
- # may be applied to any widget.
- widget = qApp.focusWidget()
- if anywidget or (
- widget == self.regexMultiLineEdit or
- widget == self.stringMultiLineEdit or
- widget == self.replaceTextEdit or
- widget == self.codeTextBrowser):
- try:
- eval("widget.%s" % methodstr)
- except:
- pass
-
-
- def editUndo(self):
- self.widgetMethod("undo()")
-
- def editRedo(self):
- self.widgetMethod("redo()")
-
- def editCopy(self):
- self.widgetMethod("copy()", 1)
-
- def editCut(self):
- self.widgetMethod("cut()")
-
- def editPaste(self):
- self.widgetMethod("paste()")
-
-
- def preferences(self):
- self.prefs.showPrefsDialog()
- self.prefs.prefsSaved.connect(self.prefsSaved)
-
- def setfont(self, font):
- self.regexMultiLineEdit.setFont(font)
- self.stringMultiLineEdit.setFont(font)
- self.replaceTextEdit.setFont(font)
-
- def setMatchFont(self, font):
- self.groupTable.setFont(font)
- self.matchTextBrowser.setFont(font)
- self.matchAllTextBrowser.setFont(font)
- self.replaceTextBrowser.setFont(font)
- self.codeTextBrowser.setFont(font)
-
-
- def getfont(self):
- return self.regexMultiLineEdit.font()
-
-
- def getMatchFont(self):
- return self.groupTable.font()
-
-
- def helpHelp(self):
- self.helpWindow = help.Help(self, "kodos.html")
-
-
- def helpPythonRegex(self):
- self.helpWindow = help.Help(self, os.path.join("python", "module-re.html"))
-
-
- def helpRegexLib(self):
- f = os.path.join("help", "regex-lib.xml")
- self.regexlibwin = RegexLibrary(f)
- self.regexlibwin.pasteRegexLib.connect(self.pasteFromRegexLib)
- self.regexlibwin.show()
-
-
- def helpAbout(self):
- self.aboutWindow = About()
- self.aboutWindow.show()
-
-
- def kodos_website(self):
- self.launch_browser_wrapper("http://kodos.sourceforge.net")
-
-
- def check_for_update(self):
- url = "http://sourceforge.net/project/showfiles.php?group_id=43860"
- try:
- fp = urllib.urlopen(url)
- except:
- self.status_bar.set_message(self.tr("Failed to open url"),
- 5,
- TRUE)
- return
-
- lines = fp.readlines()
- html = string.join(lines)
-
- rawstr = r"""kodos-(?P.*?)\.\w{3,4}\<"""
- match_obj = re.search(rawstr, html)
- if match_obj:
- latest_version = match_obj.group('version')
- if latest_version == VERSION:
- QMessageBox.information(None,
- self.tr("No Update is Available"),
- unicode(self.tr("You are currently using the latest version of Kodos")) + " (%s)" % VERSION)
- else:
- message = "%s\n\n%s: %s.\n%s: %s.\n\n%s\n" % \
- (unicode(self.tr("There is a newer version of Kodos available.")),
- unicode(self.tr("You are using version:")),
- VERSION,
- unicode(self.tr("The latest version is:")),
- latest_version,
- unicode(self.tr("Press OK to launch browser")))
-
- self.launch_browser_wrapper(url,
- self.tr("Kodos Update Available"),
- message)
- else:
- message = "%s.\n\n%s" % \
- (unicode(self.tr("Unable to get version info from Sourceforge")),
- unicode(self.tr("Press OK to launch browser")))
- self.launch_browser_wrapper(url,
- self.tr("Unknown version available"),
- message)
-
-
- def launch_browser_wrapper(self, url, caption=None, message=None):
- if launch_browser(url, caption, message):
- self.status_bar.set_message(self.tr("Launching web browser"),
- 3,
- TRUE)
- else:
- self.status_bar.set_message(self.tr("Cancelled web browser launch"),
- 3,
- TRUE)
-
-
- def reference_guide(self):
- self.ref_win = Reference(self)
- self.ref_win.pasteSymbol.connect(self.paste_symbol)
- self.ref_win.show()
-
-
- def report_bug(self):
- self.bug_report_win = reportBugWindow(self)
-
-
-##############################################################################
-#
-#
-##############################################################################
+from modules.main import Kodos
+from modules.util import findFile
def usage():
print "kodos.py [-f filename | --file=filename ] [ -d debug | --debug=debug ] [ -k kodos_dir ]"
@@ -1097,20 +63,20 @@ def main():
os.environ['KODOS_DIR'] = kodos_dir
- qApp = QApplication(sys.argv)
+ qApp = QtGui.QApplication(sys.argv)
qApp.setOrganizationName("kodos")
qApp.setApplicationName("kodos")
qApp.setOrganizationDomain("kodos.sourceforge.net")
if locale not in (None, 'en'):
- localefile = "kodos_%s.qm" % (locale or QTextCodec.locale())
+ localefile = "kodos_%s.qm" % (locale or QtCore.QTextCodec.locale())
localepath = findFile(os.path.join("translations", localefile))
if debug:
print "locale changed to:", locale
print localefile
print localepath
- translator = QTranslator(qApp)
+ translator = QtCore.QTranslator(qApp)
translator.load(localepath)
qApp.installTranslator(translator)
diff --git a/modules/kodosBA.ui b/modules/kodosBA.ui
index a600de2..14139b3 100644
--- a/modules/kodosBA.ui
+++ b/modules/kodosBA.ui
@@ -406,7 +406,6 @@ database. New in Python version 2.0.
-
@@ -1285,22 +1284,6 @@ database. New in Python version 2.0.
-
- helpCheckForUpdateAction
- activated()
- KodosBA
- check_for_update()
-
-
- -1
- -1
-
-
- 20
- 20
-
-
-
replaceTextEdit
textChanged()
diff --git a/modules/main.py b/modules/main.py
new file mode 100644
index 0000000..e97f498
--- /dev/null
+++ b/modules/main.py
@@ -0,0 +1,991 @@
+# -*- coding: utf-8 -*-
+
+import re
+import types
+import signal
+import string
+import urllib
+import cPickle
+
+from PyQt4.QtGui import *
+from PyQt4.QtCore import *
+
+from kodosBA import *
+from util import *
+from about import *
+from . import help
+from status_bar import *
+from reference import *
+from prefs import *
+from version import VERSION
+from recent_files import RecentFiles
+from urlDialog import URLDialog
+from regexLibrary import RegexLibrary
+from newUserDialogBA import NewUserDialog
+from flags import reFlag, reFlagList
+
+# match status
+MATCH_NA = 0
+MATCH_OK = 1
+MATCH_FAIL = 2
+MATCH_PAUSED = 3
+MATCH_EXAMINED = 4
+
+TRUE = 1
+FALSE = 0
+
+TIMEOUT = 3
+
+# regex to find special flags which must begin at beginning of line
+# or after some spaces
+EMBEDDED_FLAGS = r"^ *\(\?(?P[iLmsux]*)\)"
+
+RX_BACKREF = re.compile(r"""\\\d""")
+
+STATE_UNEDITED = 0
+STATE_EDITED = 1
+
+GEO = "kodos_geometry"
+
+# colors for normal & examination mode
+QCOLOR_WHITE = QColor(Qt.white) # normal
+QCOLOR_YELLOW = QColor(255,255,127) # examine
+
+try:
+ signal.SIGALRM
+ HAS_ALARM = 1
+except:
+ HAS_ALARM = 0
+
+
+##############################################################################
+#
+# The Kodos class which defines the main functionality and user interaction
+#
+##############################################################################
+
+class Kodos(KodosBA):
+ def __init__(self, filename, debug):
+ KodosBA.__init__(self)
+
+ self.debug = debug
+ self.regex = ""
+ self.matchstring = ""
+ self.replace = ""
+ self.is_paused = 0
+ self.is_examined = 0
+ self.filename = ""
+ self.match_num = 1 # matches are labeled 1..n
+ self.replace_num = 0 # replace all
+ self.url = None
+ self.group_tuples = None
+ self.editstate = STATE_UNEDITED
+
+ self.ref_win = None
+ self.regexlibwin = None
+
+ self.embedded_flags_obj = re.compile(EMBEDDED_FLAGS)
+ self.regex_embedded_flags_removed = ""
+
+ self.createStatusBar()
+
+ self.MSG_NA = self.tr("Enter a regular expression and a string to match against")
+ self.MSG_PAUSED = self.tr("Kodos regex processing is paused. Click the pause icon to unpause")
+ self.MSG_FAIL = self.tr("Pattern does not match")
+
+
+ self.statusPixmapsDict = { MATCH_NA: QPixmap(":images/yellow.png"),
+ MATCH_OK: QPixmap(":images/green.png"),
+ MATCH_FAIL: QPixmap(":images/red.png"),
+ MATCH_PAUSED: QPixmap(":images/pause.png"),
+ }
+
+
+ self.updateStatus(self.MSG_NA, MATCH_NA)
+
+ self.reFlags = reFlagList([
+ reFlag("re.IGNORECASE", "i", self.ignorecaseCheckBox),
+ reFlag("re.MULTILINE", "m", self.multilineCheckBox),
+ reFlag("re.DOTALL", "s", self.dotallCheckBox),
+ reFlag("re.VERBOSE", "x", self.verboseCheckBox),
+ reFlag("re.LOCALE", "L", self.localeCheckBox),
+ reFlag("re.UNICODE", "u", self.unicodeCheckBox),
+ ])
+ self.reFlags.clearAll()
+
+ restoreWindowSettings(self, GEO)
+
+ self.show()
+
+ self.prefs = Preferences(self, 1)
+ self.recent_files = RecentFiles(self,
+ self.prefs.recentFilesSpinBox.value(),
+ self.debug)
+
+ if filename and self.openFile(filename):
+ qApp.processEvents()
+
+ self.fileMenu.triggered.connect(self.fileMenuHandler)
+
+ kodos_toolbar_logo(self.toolBar)
+ if self.replace: self.show_replace_widgets()
+ else: self.hide_replace_widgets()
+
+ self.checkIfNewUser()
+
+
+ def checkIfNewUser(self):
+ s = QSettings()
+ if s.value('New User', "true").toPyObject() != "false":
+ self.newuserdialog = NewUserDialog()
+ self.newuserdialog.show()
+ s.setValue('New User', "false")
+
+
+ def createStatusBar(self):
+ self.status_bar = Status_Bar(self, FALSE, "")
+
+
+ def updateStatus(self, status_string, status_value, duration=0, replace=FALSE, tooltip=''):
+ pixmap = self.statusPixmapsDict.get(status_value)
+
+ self.status_bar.set_message(status_string, duration, replace, tooltip, pixmap)
+
+
+ def fileMenuHandler(self, menuid):
+ if self.recent_files.isRecentFile(menuid):
+ fn = str(menuid.text())
+ if self.openFile(fn):
+ self.recent_files.add(fn)
+
+ def prefsSaved(self):
+ if self.debug: print "prefsSaved slot"
+ self.recent_files.setNumShown(self.prefs.recentFilesSpinBox.value())
+
+
+ def kodos_edited_slot(self):
+ # invoked whenever the user has edited something
+ self.editstate = STATE_EDITED
+
+
+ def checkbox_slot(self):
+ self.process_regex()
+
+
+ def set_flags(self, flags):
+ # from the given integer value of flags, set the checkboxes
+ # this is used when loading a saved file
+ for f in self.reFlags:
+ f.checkBox.setChecked(flags & f.reFlag)
+
+
+ def get_flags_string(self):
+ flags_str = ""
+
+ for f in self.reFlags:
+ if f.checkBox.isChecked():
+ flags_str += "| " + f.flagName
+
+ if flags_str:
+ flags_str = ", " + flags_str[1:]
+ return flags_str
+
+
+ def get_embedded_flags_string(self):
+ flags_str = flags = ""
+
+ for f in self.reFlags:
+ if f.checkBox.isChecked():
+ flags += f.shortFlag
+
+ if flags:
+ flags_str = "(?" + flags + ")"
+
+ return flags_str
+
+
+ def pause(self):
+ self.is_paused = not self.is_paused
+ if self.debug: print "is_paused:", self.is_paused
+
+ if self.is_paused:
+ self.update_results(self.MSG_PAUSED, MATCH_PAUSED)
+ self.matchNumberSpinBox.setDisabled(1)
+
+ else:
+ self.process_regex()
+ self.matchNumberSpinBox.setEnabled(1)
+
+
+ def examine(self):
+ self.is_examined = not self.is_examined
+ if self.debug: print "is_examined:", self.is_examined
+
+ if self.is_examined:
+ color = QCOLOR_YELLOW
+ regex = self.regex
+ self.regex_saved = self.regex
+ length = len(regex)
+ self.regexMultiLineEdit.setReadOnly(1)
+ self.stringMultiLineEdit.setReadOnly(1)
+ self.replaceTextEdit.setReadOnly(1)
+ for i in range(length, 0, -1):
+ regex = regex[:i]
+ self.process_embedded_flags(self.regex)
+ try:
+ m = re.search(regex, self.matchstring, self.reFlags.allFlagsORed())
+ if m:
+ if self.debug: print "examined regex:", regex
+ self.__refresh_regex_widget(color, regex)
+ return
+ except:
+ pass
+
+ self.__refresh_regex_widget(color, "")
+ else:
+ regex = self.regex_saved
+ color = QCOLOR_WHITE
+ self.regexMultiLineEdit.setReadOnly(0)
+ self.stringMultiLineEdit.setReadOnly(0)
+ self.replaceTextEdit.setReadOnly(0)
+ self.__refresh_regex_widget(color, regex)
+
+
+ def __refresh_regex_widget(self, base_qcolor, regex):
+ pal = QPalette()
+ pal.setColor(pal.Base, base_qcolor)
+ self.regexMultiLineEdit.setPalette(pal)
+
+ self.regexMultiLineEdit.blockSignals(1)
+ self.regexMultiLineEdit.clear()
+ self.regexMultiLineEdit.blockSignals(0)
+ self.regexMultiLineEdit.setPlainText(regex)
+
+
+ def match_num_slot(self, num):
+ self.match_num = num
+ self.process_regex()
+
+
+ def replace_num_slot(self, num):
+ self.replace_num = num
+ self.process_regex()
+
+
+ def regex_changed_slot(self):
+ self.regex = unicode(self.regexMultiLineEdit.toPlainText())
+ self.process_regex()
+
+
+ def string_changed_slot(self):
+ self.matchstring = unicode(self.stringMultiLineEdit.toPlainText())
+ self.process_regex()
+
+ def helpContents(self, x = None):
+ pass #FIXME
+ def helpIndex(self, x = None):
+ pass #FIXME
+
+ def hide_replace_widgets(self):
+ self.spacerLabel.hide()
+ self.replaceLabel.hide()
+ self.replaceNumberSpinBox.hide()
+ self.replaceTextBrowser.clear()
+ self.replaceTextBrowser.setDisabled(TRUE)
+
+ def show_replace_widgets(self):
+ self.spacerLabel.show()
+ self.replaceLabel.show()
+ self.replaceNumberSpinBox.show()
+ self.replaceNumberSpinBox.setEnabled(TRUE)
+ self.replaceTextBrowser.setEnabled(TRUE)
+
+ def replace_changed_slot(self):
+ self.replace = unicode(self.replaceTextEdit.toPlainText())
+ self.process_regex()
+ if not self.replace:
+ self.hide_replace_widgets()
+ else:
+ self.show_replace_widgets()
+
+
+ def update_results(self, msg, val):
+ self.updateStatus(msg, val)
+
+
+ def populate_group_table(self, tuples):
+ rows = len(tuples)
+ # Remove old rows for groups that no longer exist
+ for i in range(rows, self.groupTable.rowCount()):
+ self.groupTable.removeRow(i)
+
+ self.groupTable.setRowCount(rows)
+ row = 0
+ for t in tuples:
+ self.groupTable.setItem(row, 0, QTableWidgetItem(t[1]))
+ self.groupTable.setItem(row, 1, QTableWidgetItem(t[2]))
+ row += 1
+
+
+ def populate_code_textbrowser(self):
+ self.codeTextBrowser.clear()
+
+ code = "import re\n\n"
+ code += "# common variables\n\n"
+ code += "rawstr = r\"\"\"" + self.regex_embedded_flags_removed + "\"\"\"\n"
+ code += "embedded_rawstr = r\"\"\"" + self.get_embedded_flags_string() + \
+ self.regex_embedded_flags_removed + "\"\"\"\n"
+ code += 'matchstr = \"\"\"' + self.matchstring + '\"\"\"'
+ code += "\n\n"
+ code += "# method 1: using a compile object\n"
+ code += "compile_obj = re.compile(rawstr"
+ code += self.get_flags_string()
+ code += ")\n"
+ code += "match_obj = compile_obj.search(matchstr)\n\n"
+
+ code += "# method 2: using search function (w/ external flags)\n"
+ code += "match_obj = re.search(rawstr, matchstr"
+ code += self.get_flags_string()
+ code += ")\n\n"
+
+ code += "# method 3: using search function (w/ embedded flags)\n"
+ code += "match_obj = re.search(embedded_rawstr, matchstr)\n\n"
+
+
+ if self.group_tuples:
+ code += "# Retrieve group(s) from match_obj\n"
+ code += "all_groups = match_obj.groups()\n\n"
+ code += "# Retrieve group(s) by index\n"
+ i = 0
+ named_grps = 0
+ for grp in self.group_tuples:
+ i += 1
+ code += "group_%d = match_obj.group(%d)\n" % (i, i)
+ if grp[1]: named_grps = 1
+
+ if named_grps:
+ code += "\n# Retrieve group(s) by name\n"
+ for grp in self.group_tuples:
+ if grp[1]:
+ code += "%s = match_obj.group('%s')\n" % (grp[1], grp[1])
+
+ code += "\n"
+
+ if self.replace:
+ code += "# Replace string\n"
+ code += "newstr = compile_obj.subn('%s', %d)\n" % (self.replace,
+ self.replace_num)
+
+ self.codeTextBrowser.setPlainText(code)
+
+
+ def colorize_strings(self, strings, widget, cursorOffset=0):
+ widget.clear()
+
+ colors = (QBrush(QColor(Qt.black)), QBrush(QColor(Qt.blue)) )
+ cur = widget.textCursor()
+ format = cur.charFormat()
+
+ pos = cur.position()
+ i = 0
+ for s in strings:
+ format.setForeground(colors[i%2])
+ cur.insertText(s, format)
+ if i == cursorOffset:
+ pos = cur.position()
+ i += 1
+
+ cur.setPosition(pos)
+ widget.setTextCursor(cur)
+ widget.centerCursor()
+
+
+ def populate_match_textbrowser(self, startpos, endpos):
+ pre = post = match = ""
+
+ match = self.matchstring[startpos:endpos]
+
+ # prepend the beginning that didn't match
+ if startpos > 0:
+ pre = self.matchstring[0:startpos]
+
+ # append the end that didn't match
+ if endpos < len(self.matchstring):
+ post = self.matchstring[endpos:]
+
+ strings = [pre, match, post]
+ self.colorize_strings(strings, self.matchTextBrowser, 1)
+
+
+ def populate_replace_textbrowser(self, spans, nummatches, compile_obj):
+ self.replaceTextBrowser.clear()
+ if not spans: return
+
+ num = self.replaceNumberSpinBox.value()
+ if num == 0: num = nummatches
+ text = self.matchstring
+
+ replace_text = unicode(self.replaceTextEdit.toPlainText())
+ if RX_BACKREF.search(replace_text):
+ # if the replace string contains a backref we just use the
+ # python regex methods for the substitution
+ replaced = compile_obj.subn(replace_text, text, num)[0]
+ self.replaceTextBrowser.setPlainText(replaced)
+ return
+
+ numreplaced = idx = 0
+
+ strings = []
+
+ for span in spans:
+ if span[0] != 0:
+ s = text[idx:span[0]]
+ else:
+ s = ""
+
+ idx = span[1]
+ numreplaced += 1
+
+ strings.append(s)
+ strings.append(self.replace)
+
+ if numreplaced >= num:
+ strings.append(text[span[1]:])
+ break
+
+ self.colorize_strings(strings, self.replaceTextBrowser)
+
+
+ def populate_matchAll_textbrowser(self, spans):
+ self.matchAllTextBrowser.clear()
+ if not spans: return
+
+ idx = 0
+ text = self.matchstring
+ strings = []
+ for span in spans:
+ if span[0] != 0:
+ s = text[idx:span[0]]
+ else:
+ s = ""
+
+ idx = span[1]
+ strings.append(s)
+ strings.append(text[span[0]:span[1]])
+
+ if 0 <= idx <= len(text):
+ strings.append(text[span[1]:])
+
+ self.colorize_strings(strings, self.matchAllTextBrowser)
+
+
+ def clear_results(self):
+ # .clear() destroys the headers, and .clearContents() doesn't do
+ # anything at all, so remove the rows one by one
+ for i in range(self.groupTable.rowCount()):
+ self.groupTable.removeRow(i)
+
+ self.codeTextBrowser.clear()
+ self.matchTextBrowser.clear()
+ self.matchNumberSpinBox.setEnabled(FALSE)
+ self.replaceNumberSpinBox.setEnabled(FALSE)
+ self.replaceTextBrowser.clear()
+ self.matchAllTextBrowser.clear()
+
+
+ def process_regex(self):
+ def timeout(signum, frame):
+ return
+
+ if self.is_paused:
+ return
+
+ self.process_embedded_flags(self.regex)
+
+ if not self.regex or not self.matchstring:
+ self.update_results(self.MSG_NA, MATCH_NA)
+ self.clear_results()
+ return
+
+ if HAS_ALARM:
+ signal.signal(signal.SIGALRM, timeout)
+ signal.alarm(TIMEOUT)
+
+ try:
+ compile_obj = re.compile(self.regex, self.reFlags.allFlagsORed())
+ allmatches = compile_obj.findall(self.matchstring)
+
+ if allmatches and len(allmatches):
+ self.matchNumberSpinBox.setMaximum(len(allmatches))
+ self.matchNumberSpinBox.setEnabled(TRUE)
+ self.replaceNumberSpinBox.setMaximum(len(allmatches))
+ self.replaceNumberSpinBox.setEnabled(TRUE)
+ else:
+ self.matchNumberSpinBox.setEnabled(FALSE)
+ self.replaceNumberSpinBox.setEnabled(FALSE)
+
+ match_obj = compile_obj.search(self.matchstring)
+
+ except Exception, e:
+ self.update_results(unicode(e), MATCH_FAIL)
+ return
+
+ if HAS_ALARM:
+ signal.alarm(0)
+
+ if not match_obj:
+ self.update_results(self.MSG_FAIL, MATCH_FAIL)
+
+ self.clear_results()
+ return
+
+ # match_index is the list element for match_num.
+ # Therefor match_num is for ui display
+ # and match_index is for application logic.
+ match_index = self.match_num - 1
+
+ if match_index > 0:
+ for i in range(match_index):
+ match_obj = compile_obj.search(self.matchstring,
+ match_obj.end())
+
+ self.populate_match_textbrowser(match_obj.start(), match_obj.end())
+
+ self.group_tuples = []
+
+ if match_obj.groups():
+ group_nums = {}
+ if compile_obj.groupindex:
+ keys = compile_obj.groupindex.keys()
+ for key in keys:
+ group_nums[compile_obj.groupindex[key]] = key
+
+ if self.debug:
+ print "group_nums:", group_nums
+ print "grp index: ", compile_obj.groupindex
+ print "groups:", match_obj.groups()
+ print "span: ", match_obj.span()
+
+ # create group_tuple in the form: (group #, group name, group matches)
+ g = allmatches[match_index]
+ if type(g) == types.TupleType:
+ for i in range(len(g)):
+ group_tuple = (i+1, group_nums.get(i+1, ""), g[i])
+ self.group_tuples.append(group_tuple)
+ else:
+ self.group_tuples.append( (1, group_nums.get(1, ""), g) )
+
+ self.populate_group_table(self.group_tuples)
+ else:
+ # clear the group table
+ self.populate_group_table([])
+
+ str_pattern_matches = unicode(self.tr("Pattern matches"))
+ str_found = unicode(self.tr("found"))
+ str_match = unicode(self.tr("match"))
+ str_matches = unicode(self.tr("matches"))
+
+ if len(allmatches) == 1:
+ status = "%s (%s 1 %s)" % (str_pattern_matches,
+ str_found,
+ str_match)
+ else:
+ status = "%s (%s %d %s)" % (str_pattern_matches,
+ str_found,
+ len(allmatches),
+ str_matches)
+
+ self.update_results(status, MATCH_OK)
+ self.populate_code_textbrowser()
+
+ spans = self.findAllSpans(compile_obj)
+ if self.replace:
+ self.populate_replace_textbrowser(spans, len(allmatches), compile_obj)
+ self.populate_matchAll_textbrowser(spans)
+
+
+ def findAllSpans(self, compile_obj):
+ spans = []
+
+ match_obj = compile_obj.search(self.matchstring)
+
+ last_span = None
+
+ while match_obj:
+ start = match_obj.start()
+ end = match_obj.end()
+ span = (start, end)
+ if last_span == span: break
+
+ spans.append(span)
+
+ last_span = span
+ match_obj = compile_obj.search(self.matchstring, end)
+
+ return spans
+
+
+ def closeEvent(self, ev):
+ if not self.checkEditState():
+ ev.ignore()
+ return
+
+ saveWindowSettings(self, GEO)
+
+ try:
+ self.regexlibwin.close()
+ except:
+ pass
+
+ try:
+ self.ref_win.close()
+ except:
+ pass
+ ev.accept()
+
+
+ def fileNew(self):
+ if not self.checkEditState():
+ return
+ self.filename = ""
+
+ self.regexMultiLineEdit.setPlainText("")
+ self.stringMultiLineEdit.setPlainText("")
+ self.replaceTextEdit.setPlainText("")
+ self.set_flags(0)
+ self.editstate = STATE_UNEDITED
+
+
+ def importURL(self):
+ self.urldialog = URLDialog(self, self.url)
+ self.urldialog.urlImported.connect(self.urlImported)
+
+
+ def urlImported(self, html, url):
+ self.url = url
+ self.stringMultiLineEdit.setPlainText(html)
+
+
+ def importFile(self):
+ fn = QFileDialog.getOpenFileName(self,
+ self.tr("Import File"),
+ self.filename,
+ self.tr("All (*)"))
+
+ if fn.isEmpty():
+ self.updateStatus(self.tr("A file was not selected for import"),
+ -1,
+ 5,
+ TRUE)
+ return None
+
+ filename = str(fn)
+
+ try:
+ fp = open(filename, "r")
+ except:
+ msg = self.tr("Could not open file for reading: ") + filename
+ self.updateStatus(msg, -1, 5, TRUE)
+ return None
+
+ data = fp.read()
+ fp.close()
+ self.stringMultiLineEdit.setPlainText(data)
+
+
+ def fileOpen(self):
+ filename = self.filename
+ if filename == None:
+ filename = ""
+ fn = QFileDialog.getOpenFileName(self,
+ self.tr("Open Kodos File"),
+ filename,
+ self.tr("Kodos file (*.kds);;All (*)"))
+ if not fn.isEmpty():
+ filename = str(fn)
+ if self.openFile(filename):
+ self.recent_files.add(filename)
+
+
+ def openFile(self, filename):
+ if not self.checkEditState():
+ return
+
+ self.filename = None
+
+ try:
+ fp = open(filename, "r")
+ except:
+ msg = self.tr("Could not open file for reading: ") + filename
+ self.updateStatus(msg, -1, 5, TRUE)
+ return None
+
+ try:
+ u = cPickle.Unpickler(fp)
+ self.regex = u.load()
+ self.matchstring = u.load()
+ flags = u.load()
+ except Exception, e: #FIXME: don't catch everything
+ if self.debug:
+ print unicode(e)
+ msg = "%s %s" % (unicode(self.tr("Error reading from file:")),
+ filename)
+ self.updateStatus(msg, -1, 5, TRUE)
+ return 0
+
+ self.matchNumberSpinBox.setValue(1)
+ self.regexMultiLineEdit.setPlainText(self.regex)
+ self.stringMultiLineEdit.setPlainText(self.matchstring)
+
+ self.set_flags(flags)
+
+ try:
+ replace = u.load()
+ except:
+ # versions prior to 1.7 did not have replace functionality
+ # so kds files saved w/ these versions will throw exception
+ # here.
+ replace = ""
+ self.replaceTextEdit.setPlainText(replace)
+
+ self.filename = filename
+ msg = "%s %s" % (filename, unicode(self.tr("loaded successfully")))
+ self.updateStatus(msg, -1, 5, TRUE)
+ self.editstate = STATE_UNEDITED
+ return 1
+
+
+ def fileSaveAs(self):
+ filename = self.filename
+ if filename == None:
+ filename = ""
+ filedialog = QFileDialog(self,
+ self.tr("Save Kodos File"),
+ filename,
+ "Kodos file (*.kds);;All (*)")
+ filedialog.setAcceptMode(QFileDialog.AcceptSave)
+ filedialog.setDefaultSuffix("kds")
+ ok = filedialog.exec_()
+
+ if ok == QDialog.Rejected:
+ self.updateStatus(self.tr("No file selected to save"), -1, 5, TRUE)
+ return
+
+ filename = os.path.normcase(unicode(filedialog.selectedFiles().first()))
+
+ self.filename = filename
+ self.fileSave()
+
+
+ def fileSave(self):
+ if not self.filename:
+ self.fileSaveAs()
+ return
+
+ try:
+ fp = open(self.filename, "w")
+ except:
+ msg = "%s: %s" % (unicode(self.tr("Could not open file for writing:")),
+ self.filename)
+ self.updateStatus(msg, -1, 5, TRUE)
+ return None
+
+ self.editstate = STATE_UNEDITED
+ p = cPickle.Pickler(fp)
+ p.dump(self.regex)
+ p.dump(self.matchstring)
+ p.dump(self.reFlags.allFlagsORed())
+ p.dump(self.replace)
+
+ fp.close()
+ msg = "%s %s" % (unicode(self.filename),
+ unicode(self.tr("successfully saved")))
+ self.updateStatus(msg, -1, 5, TRUE)
+ self.recent_files.add(self.filename)
+
+
+ def paste_symbol(self, symbol):
+ self.regexMultiLineEdit.insertPlainText(symbol)
+
+
+ def process_embedded_flags(self, regex):
+ # determine if the regex contains embedded regex flags.
+ # if it does, set the appropriate checkboxes on the UI to reflect the flags that are embedded
+ match = self.embedded_flags_obj.match(regex)
+ if not match:
+ embedded_flags = ""
+ self.regex_embedded_flags_removed = regex
+ else:
+ embedded_flags = match.group('flags')
+ self.regex_embedded_flags_removed = self.embedded_flags_obj.sub("", regex, 1)
+
+ for f in self.reFlags:
+ if f.shortFlag in embedded_flags:
+ f.embed()
+ else:
+ f.deembed()
+
+
+ def checkEditState(self):
+ if self.editstate == STATE_EDITED:
+ message = self.tr("You have made changes. Would you like to save them before continuing?")
+
+ prompt = QMessageBox.warning(None,
+ self.tr("Save changes?"),
+ message,
+ QMessageBox.Save |
+ QMessageBox.Cancel |
+ QMessageBox.Discard)
+
+ if prompt == QMessageBox.Cancel:
+ return False
+
+ if prompt == QMessageBox.Save:
+ self.fileSave()
+ if not self.filename: self.checkEditState()
+
+ return True
+
+
+ def pasteFromRegexLib(self, d):
+ if not self.checkEditState():
+ return
+
+ self.filename = ""
+
+ self.regexMultiLineEdit.setPlainText(d.get('regex', ""))
+ self.stringMultiLineEdit.setPlainText(d.get('text', ""))
+ self.replaceTextEdit.setPlainText(d.get('replace', ""))
+
+ try:
+ # set the current page if applicable
+ self.resultTabWidget.setCurrentIndex(int(d['tab']))
+ except KeyError:
+ pass
+ self.editstate = STATE_UNEDITED
+
+
+ def revert_file_slot(self):
+ if not self.filename:
+ self.updateStatus(self.tr("There is no filename to revert"),
+ -1,
+ 5,
+ TRUE)
+ return
+
+ self.openFile(self.filename)
+
+
+ def getWidget(self):
+ widget = qApp.focusWidget()
+ if (widget == self.regexMultiLineEdit or
+ widget == self.stringMultiLineEdit or
+ widget == self.replaceTextEdit or
+ widget == self.codeTextBrowser):
+ return widget
+ else:
+ return None
+
+
+ def widgetMethod(self, methodstr, anywidget=0):
+ # execute the methodstr of widget only if widget
+ # is one of the editable widgets OR if the method
+ # may be applied to any widget.
+ widget = qApp.focusWidget()
+ if anywidget or (
+ widget == self.regexMultiLineEdit or
+ widget == self.stringMultiLineEdit or
+ widget == self.replaceTextEdit or
+ widget == self.codeTextBrowser):
+ try:
+ eval("widget.%s" % methodstr)
+ except:
+ pass
+
+
+ def editUndo(self):
+ self.widgetMethod("undo()")
+
+ def editRedo(self):
+ self.widgetMethod("redo()")
+
+ def editCopy(self):
+ self.widgetMethod("copy()", 1)
+
+ def editCut(self):
+ self.widgetMethod("cut()")
+
+ def editPaste(self):
+ self.widgetMethod("paste()")
+
+
+ def preferences(self):
+ self.prefs.showPrefsDialog()
+ self.prefs.prefsSaved.connect(self.prefsSaved)
+
+ def setfont(self, font):
+ self.regexMultiLineEdit.setFont(font)
+ self.stringMultiLineEdit.setFont(font)
+ self.replaceTextEdit.setFont(font)
+
+ def setMatchFont(self, font):
+ self.groupTable.setFont(font)
+ self.matchTextBrowser.setFont(font)
+ self.matchAllTextBrowser.setFont(font)
+ self.replaceTextBrowser.setFont(font)
+ self.codeTextBrowser.setFont(font)
+
+
+ def getfont(self):
+ return self.regexMultiLineEdit.font()
+
+
+ def getMatchFont(self):
+ return self.groupTable.font()
+
+
+ def helpHelp(self):
+ self.helpWindow = help.Help(self, "kodos.html")
+
+
+ def helpPythonRegex(self):
+ self.helpWindow = help.Help(self, os.path.join("python", "module-re.html"))
+
+
+ def helpRegexLib(self):
+ f = os.path.join("help", "regex-lib.xml")
+ self.regexlibwin = RegexLibrary(f)
+ self.regexlibwin.pasteRegexLib.connect(self.pasteFromRegexLib)
+ self.regexlibwin.show()
+
+
+ def helpAbout(self):
+ self.aboutWindow = About()
+ self.aboutWindow.show()
+
+
+ def kodos_website(self):
+ self.launch_browser_wrapper('https://github.com/luksan/kodos',
+ message=self.tr('Launch web browser to go to the kodos project page?'))
+
+
+ def launch_browser_wrapper(self, url, caption=None, message=None):
+ if launch_browser(url, caption, message):
+ self.status_bar.set_message(self.tr("Launching web browser"),
+ 3,
+ TRUE)
+ else:
+ self.status_bar.set_message(self.tr("Cancelled web browser launch"),
+ 3,
+ TRUE)
+
+
+ def reference_guide(self):
+ self.ref_win = Reference(self)
+ self.ref_win.pasteSymbol.connect(self.paste_symbol)
+ self.ref_win.show()
+
+
+ def report_bug(self):
+ self.launch_browser_wrapper('https://github.com/luksan/kodos/issues',
+ message=self.tr("Launch web browser to report a bug in kodos?"))
diff --git a/modules/prefs.py b/modules/prefs.py
index 56ea409..3112d49 100644
--- a/modules/prefs.py
+++ b/modules/prefs.py
@@ -27,8 +27,6 @@ def load(self):
self.parent.setfont(setting.toPyObject())
if preference == 'Match Font':
self.parent.setMatchFont(setting.toPyObject())
- if preference == 'Email Server':
- self.emailServerEdit.setText(setting.toPyObject())
if preference == 'Recent Files Count':
self.recentFilesSpinBox.setValue(int(setting.toPyObject()))
except Exception, e:
@@ -39,7 +37,6 @@ def load(self):
def save(self):
self.settings.setValue('Font', self.parent.getfont())
self.settings.setValue('Match Font', self.parent.getMatchFont())
- self.settings.setValue('Email Server', self.emailServerEdit.text())
self.settings.setValue('Recent Files Count', self.recentFilesSpinBox.text())
self.settings.sync()
diff --git a/modules/prefsBA.ui b/modules/prefsBA.ui
index c5c8586..d2f2290 100644
--- a/modules/prefsBA.ui
+++ b/modules/prefsBA.ui
@@ -7,7 +7,7 @@
0
0
540
- 268
+ 145
@@ -22,7 +22,7 @@
11
6
519
- 246
+ 131
@@ -44,13 +44,6 @@
- -
-
-
-
-
-
-
-
@@ -61,18 +54,22 @@
- -
+
-
- -
-
+
-
+
+
+
+
+
- -
-
+
-
+
0
@@ -80,30 +77,30 @@
- Email Server:
+ Recent Files:
false
- -
-
+
-
+
-
+
0
0
-
- Recent Files:
+
+ 25
-
- false
+
+ 5
- -
+
-
Qt::Horizontal
@@ -119,40 +116,8 @@
- -
-
-
-
- 0
- 0
-
-
-
- 25
-
-
- 5
-
-
-
- -
-
-
- Qt::Vertical
-
-
- QSizePolicy::Expanding
-
-
-
- 20
- 40
-
-
-
-
-
@@ -228,8 +193,6 @@
fontButton
- emailServerEdit
- recentFilesSpinBox
buttonHelp
buttonApply
buttonOk
diff --git a/modules/reportBug.py b/modules/reportBug.py
deleted file mode 100644
index e4351e4..0000000
--- a/modules/reportBug.py
+++ /dev/null
@@ -1,104 +0,0 @@
-# -*- coding: utf-8 -*-
-# reportBug.py: -*- Python -*- DESCRIPTIVE TEXT.
-
-from reportBugBA import reportBugBA
-from util import *
-from PyQt4.QtGui import *
-from PyQt4.QtCore import *
-from PyQt4 import *
-import sys
-import string
-import smtplib
-from version import VERSION
-
-AUTHOR_ADDR = "phil_schwartz@users.sourceforge.net"
-
-class reportBug(reportBugBA):
- def __init__(self, parent=None, name=None):
- reportBugBA.__init__(self, parent)
- self.parent = parent
- self.kodos_main = parent.kodos_main
- self.populate()
-
-
- def populate(self):
- self.OSEdit.setText(sys.platform)
- pyvers = string.replace(sys.version, "\n", " - ")
- self.pythonVersionEdit.setText(pyvers)
- self.PyQtVersionEdit.setText(QT_VERSION_STR)
- self.regexMultiLineEdit.setPlainText(self.kodos_main.regexMultiLineEdit.toPlainText())
- self.stringMultiLineEdit.setPlainText(self.kodos_main.stringMultiLineEdit.toPlainText())
-
-
- def cancel_slot(self):
- self.parent.close()
-
- def submit_slot(self):
- addr = str(self.emailAddressEdit.text())
- if not addr:
- msg = self.tr(
- "An email address is necessary so that the author "
- "can contact you. Your email address will not "
- "be used for any other purposes.")
-
- QMessageBox.information(None,
- self.tr("You must supply a valid email address"),
- msg)
- return
-
- msg = "Subject: Kodos bug report\n\n"
- msg += "Kodos Version: %s\n" % VERSION
- msg += "Operating System: %s\n" % unicode(self.OSEdit.text())
- msg += "Python Version: %s\n" % unicode(self.pythonVersionEdit.text())
- msg += "PyQt Version: %s\n" % unicode(self.PyQtVersionEdit.text())
- msg += "\n" + "=" * 70 + "\n"
- msg += "Regex:\n%s\n" % unicode(self.regexMultiLineEdit.text())
- msg += "=" * 70 + "\n"
- msg += "String:\n%s\n" % unicode(self.stringMultiLineEdit.text())
- msg += "=" * 70 + "\n"
- msg += "Comments:\n%s\n" % unicode(self.commentsMultiLineEdit.text())
- email_server = unicode(self.kodos_main.prefs.emailServerEdit.text()) or "localhost"
- try:
- server = smtplib.SMTP(email_server)
- server.sendmail(addr, AUTHOR_ADDR, msg)
- server.quit()
- QMessageBox.information(None,
- self.tr("Bug report sent"),
- self.tr("Your bug report has been sent."))
- self.parent.close()
- except Exception, e:
- QMessageBox.information(None,
- self.tr("An exception occurred sending bug report"),
- str(e))
-
-
-class reportBugWindow(QMainWindow):
- def __init__(self, kodos_main):
- self.kodos_main = kodos_main
- QMainWindow.__init__(self, kodos_main)#, Qt.Window | Qt.WA_DeleteOnClose)
-
- self.setGeometry(100, 50, 800, 600)
- self.setWindowTitle(self.tr("Report a Bug"))
- self.setWindowIcon(QIcon(QPixmap(":images/kodos_icon.png")))
-
- self.bug_report = reportBug(self)
- self.setCentralWidget(self.bug_report)
-
-
- self.createMenu()
- self.createToolBar()
-
- self.show()
-
-
- def createMenu(self):
- self.menubar = self.menuBar()
- self.filemenu = self.menubar.addMenu(self.tr("&File"))
- self.filemenu.addAction(self.tr("&Close"), self, SLOT("close()"))
-
-
- def createToolBar(self):
- toolbar = QToolBar()
- self.addToolBar(toolbar)
- self.logolabel = kodos_toolbar_logo(toolbar)
-
diff --git a/modules/reportBugBA.ui b/modules/reportBugBA.ui
deleted file mode 100644
index 74b2eec..0000000
--- a/modules/reportBugBA.ui
+++ /dev/null
@@ -1,290 +0,0 @@
-
-
- reportBugBA
-
-
-
- 0
- 0
- 750
- 653
-
-
-
- Form1
-
-
-
- 11
-
-
- 6
-
-
-
-
-
- 6
-
-
- 0
-
-
-
-
-
- Qt::Horizontal
-
-
- QSizePolicy::Expanding
-
-
-
- 20
- 20
-
-
-
-
- -
-
-
- Submit Bug Report
-
-
-
- -
-
-
- Cancel
-
-
-
-
-
- -
-
-
- Kodos State Information
-
-
-
- 11
-
-
- 6
-
-
-
-
-
- 6
-
-
- 0
-
-
-
-
-
- Regular Expression:
-
-
- false
-
-
-
- -
-
-
- Match String:
-
-
- false
-
-
-
-
-
- -
-
-
- 6
-
-
- 0
-
-
-
-
-
- true
-
-
-
- -
-
-
- true
-
-
-
-
-
-
-
-
- -
-
-
- System Information
-
-
-
- 11
-
-
- 6
-
-
-
-
-
- Operating System:
-
-
- false
-
-
-
- -
-
-
- PyQt Version:
-
-
- false
-
-
-
- -
-
-
- Python Version:
-
-
- false
-
-
-
- -
-
-
- -
-
-
- -
-
-
-
-
-
- -
-
-
- Comments
-
-
-
- 11
-
-
- 6
-
-
-
-
-
- 0
-
-
- 6
-
-
-
-
-
- Comments:
-
-
- false
-
-
-
- -
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Email address:
-
-
- false
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
- qPixmapFromMimeSource
-
- OSEdit
- pythonVersionEdit
- PyQtVersionEdit
- emailAddressEdit
- submitButton
- cancelButton
-
-
-
-
- submitButton
- clicked()
- reportBugBA
- submit_slot()
-
-
- 20
- 20
-
-
- 20
- 20
-
-
-
-
- cancelButton
- clicked()
- reportBugBA
- cancel_slot()
-
-
- 20
- 20
-
-
- 20
- 20
-
-
-
-
-
|