oem-community-qa team mailing list archive
-
oem-community-qa team
-
Mailing list archive
-
Message #00056
[Merge] lp:~oem-community-qa/checkbox-editor/bug619720 into lp:checkbox-editor
Javier Collado has proposed merging lp:~oem-community-qa/checkbox-editor/bug619720 into lp:checkbox-editor.
Requested reviews:
Javier Collado (javier.collado)
Related bugs:
#619720 Can not run a new test case created with checkbox-editor (nor can you save it) if you don't run as root
https://bugs.launchpad.net/bugs/619720
Message dialog warns user on shared directory insufficient permissions
'Save as' functionality implemented
--
https://code.launchpad.net/~oem-community-qa/checkbox-editor/bug619720/+merge/34447
Your team OEM Community QA is subscribed to branch lp:~oem-community-qa/checkbox-editor/bug619720.
=== modified file 'checkbox_editor/editor.py'
--- checkbox_editor/editor.py 2010-08-31 10:10:41 +0000
+++ checkbox_editor/editor.py 2010-09-02 16:41:06 +0000
@@ -17,7 +17,7 @@
from .treeview import Treeview
from .form import Form
from .statusbar import StatusBar
-from .util import CommandLauncher, ExceptionHandled, exception_handler
+from .util import CommandLauncher, ExceptionHandled, exception_handler, MessageDialogRunner
from . import build
@@ -63,6 +63,7 @@
'close_menuitem_activate_cb': self.close_menuitem_activate_cb,
'revert_menuitem_activate_cb': self.revert_menuitem_activate_cb,
'save_menuitem_activate_cb': self.save_menuitem_activate_cb,
+ 'save_as_menuitem_activate_cb': self.save_as_menuitem_activate_cb,
'open_testplan_menuitem_activate_cb': self.open_testplan_menuitem_activate_cb,
'save_testplan_menuitem_activate_cb': self.save_testplan_menuitem_activate_cb,
'save_testplan_as_menuitem_activate_cb': self.save_testplan_as_menuitem_activate_cb,
@@ -109,6 +110,7 @@
for widget_name in widget_names:
self.builder.get_object(widget_name).set_sensitive(True)
+ self.set_title(options.directory)
self.statusbar._write_tmp_message('Shared directory opened')
treeview = self.builder.get_object('treeview')
@@ -173,19 +175,20 @@
window.add_accel_group(group)
accelerators_data = [
- ('open_menuitem', 'o'),
- ('close_menuitem', 'w'),
- ('quit_menuitem', 'q'),
- ('save_menuitem', 's'),
- ('add_menuitem', 'a'),
- ('remove_menuitem', 'r'),
- ('preferences_menuitem', 'p'),
- ('contents_menuitem', 'F1'),
+ ('open_menuitem', '<Control>o'),
+ ('close_menuitem', '<Control>w'),
+ ('quit_menuitem', '<Control>q'),
+ ('save_menuitem', '<Control>s'),
+ ('save_as_menuitem', '<Shift><Control>s'),
+ ('add_menuitem', '<Control>a'),
+ ('remove_menuitem', '<Control>r'),
+ ('preferences_menuitem', '<Control>p'),
+ ('contents_menuitem', '<Control>F1'),
]
- for widget_name, key in accelerators_data:
+ for widget_name, keys in accelerators_data:
menuitem = self.builder.get_object(widget_name)
- accel_key, accel_mod = gtk.accelerator_parse("<Control>%s" % key)
+ accel_key, accel_mod = gtk.accelerator_parse(keys)
menuitem.add_accelerator("activate",
group,
accel_key,
@@ -238,6 +241,19 @@
return options
+ def set_title(self, directory=None):
+ """
+ Set main window title based on the opened directory
+ """
+ title = 'Checkbox Editor'
+ if directory:
+ title = '{0} - '.format(directory) + title
+ if not os.access(directory, os.F_OK | os.W_OK):
+ title = '[read only] ' + title
+ window = self.builder.get_object('window')
+ window.set_title(title)
+
+
def window_delete_event_cb(self, window, event):
"""
Delete main window
@@ -294,23 +310,26 @@
directory = dialog.get_filename()
dialog.destroy()
- if response == gtk.RESPONSE_ACCEPT:
- if model.is_valid_directory(directory):
- self.preferences.load(directory)
- if ExceptionHandled == model.load(directory):
- model.clear()
- else:
- for widget_name in ('close_menuitem', 'close_toolbutton',
- 'open_testplan_menuitem'):
- widget = self.builder.get_object(widget_name)
- widget.set_sensitive(True)
-
- for widget_name in ('push_menuitem', 'push_toolbutton',
- 'commit_menuitem', 'commit_toolbutton'):
- widget = self.builder.get_object(widget_name)
- widget.set_sensitive(False)
-
- self.statusbar._write_tmp_message('Shared directory opened')
+ if response != gtk.RESPONSE_ACCEPT:
+ return False
+
+ if model.is_valid_directory(directory):
+ self.preferences.load(directory)
+ if ExceptionHandled == model.load(directory):
+ model.clear()
+ else:
+ for widget_name in ('close_menuitem', 'close_toolbutton',
+ 'open_testplan_menuitem'):
+ widget = self.builder.get_object(widget_name)
+ widget.set_sensitive(True)
+
+ for widget_name in ('push_menuitem', 'push_toolbutton',
+ 'commit_menuitem', 'commit_toolbutton'):
+ widget = self.builder.get_object(widget_name)
+ widget.set_sensitive(False)
+
+ self.set_title(directory)
+ self.statusbar._write_tmp_message('Shared directory opened')
return False
@@ -322,7 +341,6 @@
if not self._continue_without_saving():
return False
-
treeview = self.builder.get_object('treeview')
model = treeview.get_model()
model.clear()
@@ -332,13 +350,12 @@
self.preferences.load()
widget_names = ['close_menuitem', 'close_toolbutton',
- 'save_menuitem', 'save_toolbutton',
- 'revert_menuitem', 'revert_toolbutton',
'open_testplan_menuitem', 'save_testplan_menuitem',
'save_testplan_as_menuitem', 'export_testplan_menuitem']
for widget_name in widget_names:
self.builder.get_object(widget_name).set_sensitive(False)
+ self.set_title()
self.statusbar._write_tmp_message('Shared directory closed')
return False
@@ -354,6 +371,54 @@
if saved is True:
self.statusbar._write_tmp_message('Changes saved')
+ return False
+
+
+ def save_as_menuitem_activate_cb(self, widget):
+ """
+ Save changes to a different directory
+ """
+ # Choose a destination directory
+ dialog = gtk.FileChooserDialog('Choose Destination Shared Directory',
+ buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
+ gtk.STOCK_OPEN, gtk.RESPONSE_ACCEPT),
+ action = gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
+
+ while True:
+ response = dialog.run()
+ directory = dialog.get_filename()
+
+ if response != gtk.RESPONSE_ACCEPT:
+ dialog.destroy()
+ return False
+
+ if not os.path.isdir(directory):
+ message = ("Selected directory doesn't seem to be a valid "
+ 'directory:\n{0}\n\n'
+ 'Please make sure that the path <b>exists</b> '
+ "and that it's a directory"
+ .format(directory))
+ MessageDialogRunner('Invalid directory', message).run()
+
+ elif not os.access(directory, os.F_OK | os.R_OK | os.W_OK | os.X_OK):
+ message = ('Not enough <b>permissions</b> '
+ 'to read/write/execute selected directory:\n{0}\n\n'
+ .format(directory))
+ MessageDialogRunner('Insuficcient permissions', message).run()
+
+ elif bool(os.listdir(directory)):
+ message = ("Selected directory isn't <b>empty</b>:\n{0}\n\n"
+ 'Please select an empty directory, '
+ 'so that it can be populated with new data properly'
+ .format(directory))
+ MessageDialogRunner('Not empty directory', message).run()
+ else:
+ break
+ dialog.destroy()
+
+ model = self.builder.get_object('treeview').get_model()
+ model.save_as(directory)
+ self.set_title(directory)
return False
@@ -363,18 +428,16 @@
Check if there are pending changes and ask user
if he wants to continue
"""
- # If save menuitem is sensitive,
+ # If save_as_menuitem is sensitive,
# it means there are pending (not saved) changes
- if self.builder.get_object('save_menuitem').get_property('sensitive'):
- message = ('Changes have not been saved.\n\n'
+ if self.builder.get_object('save_as_menuitem').get_property('sensitive'):
+ message = ('Changes have <b>not</b> been saved.\n\n'
'Would you like to continue?')
- dialog = gtk.MessageDialog(type=gtk.MESSAGE_WARNING,
- buttons=gtk.BUTTONS_YES_NO,
- message_format=message)
- response = dialog.run()
- dialog.destroy()
+ response = MessageDialogRunner('Changes not saved', message,
+ type=gtk.MESSAGE_WARNING,
+ buttons=gtk.BUTTONS_YES_NO).run()
- if response == gtk.RESPONSE_NO:
+ if response != gtk.RESPONSE_YES:
return False
return True
@@ -557,13 +620,10 @@
'Please note that they are not going to be available\n'
'in checkbox.\n\n'
'Would you like to continue?')
- dialog = gtk.MessageDialog(flags=gtk.DIALOG_MODAL,
- type=gtk.MESSAGE_WARNING,
- buttons=gtk.BUTTONS_YES_NO)
- dialog.set_markup(message)
- response = dialog.run()
- dialog.destroy()
- if response == gtk.RESPONSE_NO:
+ response = MessageDialogRunner('Changes not saved', message,
+ type=gtk.MESSAGE_WARNING,
+ buttons=gtk.BUTTONS_YES_NO).run()
+ if response != gtk.RESPONSE_YES:
return
treeview = self.builder.get_object('treeview')
=== modified file 'checkbox_editor/form.py'
--- checkbox_editor/form.py 2010-08-31 09:13:14 +0000
+++ checkbox_editor/form.py 2010-09-02 16:41:06 +0000
@@ -8,7 +8,7 @@
import os, re, logging, shlex
from .model import TreeStore
-from .util import CommandLauncher, SignalBlocker
+from .util import CommandLauncher, SignalBlocker, MessageDialogRunner
class Form(object):
"""
@@ -374,16 +374,12 @@
description = row[TreeStore.DESCRIPTION]
# Prompt user to confirm change
- message=('Jobs that use the {0} plugin cannot contain '
+ message=('Jobs that use the {0!r} plugin cannot contain '
'children jobs definitions, so they will <b>removed</b>. '
- 'Is that what you would like to do?'.format(repr(value)))
- dialog = gtk.MessageDialog(type=gtk.MESSAGE_WARNING,
- buttons=gtk.BUTTONS_YES_NO)
- dialog.set_title('Cannot have children')
- dialog.set_markup(message)
- response = dialog.run()
- dialog.destroy()
-
+ 'Is that what you would like to do?'.format(value))
+ response = MessageDialogRunner('Cannot have children', message,
+ type=gtk.MESSAGE_WARNING,
+ buttons=gtk.BUTTONS_YES_NO).run()
change_cancelled = False if response == gtk.RESPONSE_YES else True
if change_cancelled:
=== modified file 'checkbox_editor/glade/editor.glade'
--- checkbox_editor/glade/editor.glade 2010-08-31 09:13:14 +0000
+++ checkbox_editor/glade/editor.glade 2010-09-02 16:41:06 +0000
@@ -76,6 +76,16 @@
</object>
</child>
<child>
+ <object class="GtkImageMenuItem" id="save_as_menuitem">
+ <property name="label" translatable="yes">Save as...</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="image">save_as_image_1</property>
+ <property name="use_stock">False</property>
+ <signal name="activate" handler="save_as_menuitem_activate_cb"/>
+ </object>
+ </child>
+ <child>
<object class="GtkImageMenuItem" id="revert_menuitem">
<property name="label">gtk-revert-to-saved</property>
<property name="visible">True</property>
@@ -115,7 +125,7 @@
<property name="label" translatable="yes">Save test plan as...</property>
<property name="visible">True</property>
<property name="sensitive">False</property>
- <property name="image">save_as_image</property>
+ <property name="image">save_as_image_2</property>
<property name="use_stock">False</property>
<signal name="activate" handler="save_testplan_as_menuitem_activate_cb"/>
</object>
@@ -354,6 +364,21 @@
</packing>
</child>
<child>
+ <object class="GtkToolButton" id="save_as_toolbutton">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="tooltip_text" translatable="yes">Save changes to a different directory</property>
+ <property name="label" translatable="yes">Save as...</property>
+ <property name="use_underline">True</property>
+ <property name="stock_id">gtk-save-as</property>
+ <signal name="clicked" handler="save_as_menuitem_activate_cb"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
<object class="GtkToolButton" id="revert_toolbutton">
<property name="visible">True</property>
<property name="sensitive">False</property>
@@ -916,7 +941,11 @@
<property name="visible">True</property>
<property name="stock">gtk-save</property>
</object>
- <object class="GtkImage" id="save_as_image">
+ <object class="GtkImage" id="save_as_image_1">
+ <property name="visible">True</property>
+ <property name="stock">gtk-save-as</property>
+ </object>
+ <object class="GtkImage" id="save_as_image_2">
<property name="visible">True</property>
<property name="stock">gtk-save-as</property>
</object>
=== modified file 'checkbox_editor/model.py'
--- checkbox_editor/model.py 2010-08-31 10:10:41 +0000
+++ checkbox_editor/model.py 2010-09-02 16:41:06 +0000
@@ -10,7 +10,7 @@
from .data import DataParser, DataEncoder, InternalParsingError, Reporter
from .vc import VersionControl
-from .util import handle_exceptions, ExceptionDialog, ExceptionHandled
+from .util import handle_exceptions, ExceptionDialog, ExceptionHandled, MessageDialogRunner
class RenameOverwriteError(Exception):
@@ -58,13 +58,6 @@
self.clear()
- # Widgets that must be enabled/disabled
- # when some change happens to the model
- on_change_widget_names = ['save_menuitem', 'save_toolbutton',
- 'revert_menuitem', 'revert_toolbutton']
- self.on_change_widgets = [builder.get_object(widget_name)
- for widget_name in on_change_widget_names]
-
def clear(self):
"""
@@ -80,6 +73,13 @@
self.files_to_rename = {}
gtk.TreeStore.clear(self)
+ # Disable all save/revert options
+ for widget_name in ['save_menuitem', 'save_toolbutton',
+ 'save_as_menuitem', 'save_as_toolbutton',
+ 'revert_menuitem', 'revert_toolbutton']:
+ widget = self.builder.get_object(widget_name)
+ widget.set_sensitive(False)
+
def get_all_rows(self, parent = None):
"""
@@ -126,55 +126,84 @@
Discard changes and reload data from directory
"""
logging.info('Reverting changes')
- self.load()
-
- # Disable save/revert options
- for widget in self.on_change_widgets:
- widget.set_sensitive(False)
-
- self.files_to_remove = []
- self.files_to_rename = {}
+ self.load(self.directory)
def is_valid_directory(self, directory):
+ """
+ Return True if it's a valid directory and data should be loaded
+ In case of not enough permissions, the user might be prompted
+ to use read-only mode
+ """
+ # Make sure directory exists
+ if not os.path.isdir(directory):
+ message = ("Selected directory doesn't seem to be a valid "
+ 'shared checkbox directory:\n{0}\n\n'
+ 'Please make sure that the path <b>exists</b> '
+ "and that it's a directory"
+ .format(directory))
+ MessageDialogRunner('Invalid directory', message).run()
+ return False
+
+ # Make sure that it's possible to access to selected directory
+ if not os.access(directory, os.F_OK | os.R_OK | os.X_OK):
+ message = ('Not enough <b>permissions</b> to access selected directory:\n{0}\n\n'
+ 'Please change permissions accordingly or run checkbox-editor '
+ 'with from a different user account with sufficient permissions.'
+ .format(directory))
+ MessageDialogRunner('Insuficcient permissions', message).run()
+ return False
+
+ # Make sure that directory contains a jobs subdirectory
jobs_dir = os.path.join(directory, 'jobs')
- if not (os.path.isdir(directory)
- and os.path.isdir(jobs_dir)):
- dialog = gtk.MessageDialog(type=gtk.MESSAGE_ERROR,
- buttons=gtk.BUTTONS_OK)
- dialog.set_title('Invalid directory')
+ if not os.path.isdir(jobs_dir):
message = ("Selected directory doesn't seem to be a valid "
- 'shared checkbox directory. Please make sure '
- 'to select a directory that at least contains '
- 'a <b>jobs</b> subdirectory.')
- dialog.set_markup(message)
- dialog.run()
- dialog.destroy()
+ 'shared checkbox directory:\n{0}\n\n'
+ 'Please make sure to select a directory '
+ 'that at least contains a <b>jobs</b> subdirectory.'
+ .format(directory))
+ MessageDialogRunner('Invalid directory', message).run()
return False
+ # Make sure that it's possible to create/delete files
+ # in the selected directory
+ if not os.access(directory, os.F_OK | os.W_OK):
+ message = ('Not enough <b>permissions</b> to write files '
+ 'to selected directory:\n{0}\n\n'
+ 'Unless permissions are changed accordingly '
+ 'or checkbox-editor is executed '
+ 'from a different user account with sufficient permissions, '
+ "it won't be possible to save changes to the same directory.\n\n"
+ 'Would you like to continue?'
+ .format(directory))
+ response = MessageDialogRunner('Insuficcient permissions',
+ message,
+ type=gtk.MESSAGE_WARNING,
+ buttons=gtk.BUTTONS_YES_NO).run()
+ if response != gtk.RESPONSE_YES:
+ return False
+
return True
@handle_exceptions(InternalParsingError,
'Load operation <b>failed</b>')
- def load(self, directory = None):
+ def load(self, directory):
"""
Load data from directory
"""
self.clear()
- self.testplan = None
-
- if directory:
- self.directory = directory
- self.jobs_dir = os.path.join(directory, 'jobs')
- self.scripts_dir = os.path.join(directory, 'scripts')
- self.vc = VersionControl.get(directory,
- self.preferences.version_control['exclude'])
-
- if self.vc.has_changes():
- for widget_name in ['commit_menuitem', 'commit_toolbutton']:
- widget = self.builder.get_object(widget_name)
- widget.set_sensitive(True)
+
+ self.directory = directory
+ self.jobs_dir = os.path.join(directory, 'jobs')
+ self.scripts_dir = os.path.join(directory, 'scripts')
+ self.vc = VersionControl.get(directory,
+ self.preferences.version_control['exclude'])
+
+ if self.vc.has_changes():
+ for widget_name in ['commit_menuitem', 'commit_toolbutton']:
+ widget = self.builder.get_object(widget_name)
+ widget.set_sensitive(True)
if hasattr(self.vc, 'pull'):
for widget_name in ['pull_menuitem', 'pull_toolbutton']:
@@ -187,12 +216,12 @@
if not pulled:
return ExceptionHandled
+ self._set_on_change_widgets()
logging.info('Loading jobs: {0}'.format(self.directory))
# Little trick that makes parsing work
# when relative directories are in preferences
- if directory:
- os.chdir(directory)
+ os.chdir(directory)
job_filename_pattern = re.compile('\.txt(\.in)?$')
jobs_filenames = [os.path.join(self.jobs_dir, file)
@@ -286,6 +315,19 @@
load_job_file(file_row, job_filename)
+ def _set_on_change_widgets(self):
+ """
+ Set the names of the widgets that are subject to be activated
+ on any change in the model based on the opened directory
+ """
+ on_change_widget_names = ['save_as_menuitem', 'save_as_toolbutton',
+ 'revert_menuitem', 'revert_toolbutton']
+ if os.access(self.directory, os.F_OK | os.W_OK):
+ on_change_widget_names.extend(['save_menuitem', 'save_toolbutton'])
+ self.on_change_widgets = [self.builder.get_object(widget_name)
+ for widget_name in on_change_widget_names]
+
+
@handle_exceptions((EnvironmentError, RenameOverwriteError),
'Save operation <b>failed</b>')
def save(self):
@@ -502,6 +544,43 @@
row[self.FONT_DESCRIPTION] = pango.FontDescription()
+ def save_as(self, destination_directory):
+ """
+ Save changes to a different directory
+ """
+ source_directory = self.directory
+ self.vc.clone(destination_directory)
+
+ # Update all directory dependent variables in model
+ logging.debug('Updating internal state to use new directory: {0!r}'
+ .format(destination_directory))
+ self.directory = destination_directory
+ self.jobs_dir = os.path.join(destination_directory, 'jobs')
+ self.scripts_dir = os.path.join(destination_directory, 'scripts')
+ self.vc = VersionControl.get(destination_directory,
+ self.preferences.version_control['exclude'])
+ def change_base_directory(filename):
+ return os.path.join(destination_directory,
+ os.path.relpath(filename, source_directory))
+
+ self.files_to_remove = [change_base_directory(filename)
+ for filename in self.files_to_remove]
+ self.files_to_rename = dict([(change_base_directory(old_filename),
+ change_base_directory(new_filename))
+ for old_filename, new_filename
+ in self.files_to_rename.items()])
+
+ # Update all rows filenames
+ for row in self.get_all_rows():
+ filename = row[self.FILENAME]
+ if filename:
+ row[self.FILENAME] = change_base_directory(filename)
+
+ self.save()
+ self.preferences.directory = destination_directory
+ self._set_on_change_widgets()
+
+
def add_row(self, iterator):
"""
Add a new row to the model
=== modified file 'checkbox_editor/preferences.py'
--- checkbox_editor/preferences.py 2010-08-31 10:10:41 +0000
+++ checkbox_editor/preferences.py 2010-09-02 16:41:06 +0000
@@ -9,7 +9,7 @@
import shlex
from glob import glob
-from .util import CommandConfigParser as ConfigParser
+from .util import MessageDialogRunner, CommandConfigParser as ConfigParser
VC_WIDGET_NAMES = ('pull_on_open', 'commit_on_save', 'push_on_close',
'pull_branch', 'push_branch')
@@ -339,12 +339,7 @@
version_control)
except IOError as exception:
logging.error(exception)
- dialog = gtk.MessageDialog(type=gtk.MESSAGE_ERROR,
- buttons=gtk.BUTTONS_OK,
- message_format=str(exception))
- dialog.set_title('Error')
- dialog.run()
- dialog.destroy()
+ MessageDialogRunner('Error', str(exception)).run()
else:
self.dialog.destroy()
@@ -470,42 +465,33 @@
filename = None
response = dialog.run()
if response == gtk.RESPONSE_ACCEPT:
+ abs_filename = dialog.get_filename()
filename = os.path.relpath(dialog.get_filename(),
self.preferences.directory)
if filename.startswith(os.path.pardir):
- log_message = ('Selected filename must be '
- 'under opened directory: {0}'
- .format(self.preferences.directory))
+ log_message = ('Selected filename {0!r} must be '
+ 'under opened directory {1!r}'
+ .format(abs_filename,
+ self.preferences.directory))
logging.error(log_message)
- dialog_message = ('Selected filename must be '
- 'under opened directory:\n<b>{0}</b>'
- .format(self.preferences.directory))
- error_dialog = gtk.MessageDialog(flags=gtk.DIALOG_MODAL,
- type=gtk.MESSAGE_ERROR,
- buttons=gtk.BUTTONS_OK)
- error_dialog.set_title('Error')
- error_dialog.set_markup(dialog_message)
- error_dialog.run()
- error_dialog.destroy()
+ dialog_message = ('Selected filename:\n{0}\n'
+ 'must be under opened directory:\n{1}'
+ .format(abs_filename,
+ self.preferences.directory))
+ MessageDialogRunner('Error', dialog_message).run()
continue
if filename in filenames and filename != initial_filename:
- log_message = ('Filename is already selected: {0}'
- .format(filename))
+ log_message = ('Filename is already selected: {0!r}'
+ .format(abs_filename))
logging.error(log_message)
- dialog_message = ('Filename is already selected:\n<b>{0}</b>'
- .format(filename))
- error_dialog = gtk.MessageDialog(flags=gtk.DIALOG_MODAL,
- type=gtk.MESSAGE_ERROR,
- buttons=gtk.BUTTONS_OK)
- error_dialog.set_title('Error')
- error_dialog.set_markup(dialog_message)
- error_dialog.run()
- error_dialog.destroy()
+ MessageDialogRunner('Error',
+ 'Filename is already selected:\n{0}'
+ .format(abs_filename)).run()
continue
break
=== modified file 'checkbox_editor/treeview.py'
--- checkbox_editor/treeview.py 2010-06-03 08:49:41 +0000
+++ checkbox_editor/treeview.py 2010-09-02 16:41:06 +0000
@@ -5,6 +5,7 @@
pygtk.require("2.0")
import gtk
from .model import TreeStore
+from .util import MessageDialogRunner
import os, re, logging
@@ -99,16 +100,16 @@
self.builder.get_object('job_command_edit').set_sensitive(False)
logging.debug('Selection changed:\n'
- '- display_name: {0}\n'
- '- description: {1}\n'
- '- filename: {2}\n'
+ '- display_name: {0!r}\n'
+ '- description: {1!r}\n'
+ '- filename: {2!r}\n'
'- modified: {3}\n'
'- children_modified: {4}\n'
'- children_removed: {5}\n'
'- selected: {6}'
- .format(repr(row[TreeStore.DISPLAY_NAME]),
- repr(row[TreeStore.DESCRIPTION]),
- repr(row[TreeStore.FILENAME]),
+ .format(row[TreeStore.DISPLAY_NAME],
+ row[TreeStore.DESCRIPTION],
+ row[TreeStore.FILENAME],
row[TreeStore.MODIFIED],
row[TreeStore.CHILDREN_MODIFIED],
row[TreeStore.CHILDREN_REMOVED],
@@ -285,30 +286,20 @@
# Validate filename
if depth == 0 and not re.search('\.txt(\.in)?$', new_name):
- dialog = gtk.MessageDialog(type=gtk.MESSAGE_ERROR,
- buttons=gtk.BUTTONS_OK)
- dialog.set_title('Invalid filename')
- message = ('Invalid filename: <b>{0}</b>\n'
- 'The extension should be either <b>.txt.in</b> or <b>.txt</b>'
- .format(new_name))
- dialog.set_markup(message)
- dialog.run()
- dialog.destroy()
- return
+ MessageDialogRunner('Invalid filename',
+ 'Invalid filename: <b>{0}</b>\n'
+ 'The extension should be either <b>.txt.in</b> or <b>.txt</b>'
+ .format(new_name)).run()
+ return
# Validate name
new_name_exists = any(r for r in model.get_all_rows()
if (r[TreeStore.DISPLAY_NAME] == new_name
and r.path != row.path))
if new_name_exists:
- dialog = gtk.MessageDialog(type=gtk.MESSAGE_ERROR,
- buttons=gtk.BUTTONS_OK)
- dialog.set_title('Invalid name')
- message = ('<b>{0}</b> is already being used in another row'
- .format(new_name))
- dialog.set_markup(message)
- dialog.run()
- dialog.destroy()
+ MessageDialogRunner('Invalid name',
+ '<b>{0}</b> is already being used in another row'
+ .format(new_name)).run()
return
# Set display name
@@ -354,27 +345,21 @@
and (len(destination_row.path) > 1
or position in (gtk.TREE_VIEW_DROP_INTO_OR_BEFORE,
gtk.TREE_VIEW_DROP_INTO_OR_AFTER))):
- error_message = ('Root nodes can only be moved\n'
+ error_message = ('Root nodes can <b>only</b> be moved\n'
'to other position at the same level')
elif (len(origin_row.path) > 1 and len(destination_row.path) <= 1
and position in (gtk.TREE_VIEW_DROP_BEFORE,
gtk.TREE_VIEW_DROP_AFTER)):
- error_message = ("Job nodes can't be moved\n"
+ error_message = ("Job nodes <b>can't</b> be moved\n"
'to the root of the tree')
elif (origin_row.path == destination_row.path[:len(origin_row.path)]):
- error_message = ("Job nodes can't be moved\n"
+ error_message = ("Job nodes <b>can't</b> be moved\n"
'to a position under themselves')
# If error_message is set, then display message
# and cancel drag&drop operation
if error_message:
- dialog = gtk.MessageDialog(flags = gtk.DIALOG_MODAL,
- type = gtk.MESSAGE_ERROR,
- buttons = gtk.BUTTONS_OK,
- message_format = error_message)
- dialog.set_title('Error')
- dialog.run()
- dialog.destroy()
+ MessageDialogRunner('Error', error_message).run()
context.finish(False, False, timestamp)
return
=== modified file 'checkbox_editor/util.py'
--- checkbox_editor/util.py 2010-06-03 08:49:41 +0000
+++ checkbox_editor/util.py 2010-09-02 16:41:06 +0000
@@ -262,3 +262,35 @@
stdout, _ = process.communicate()
stdout = stdout.splitlines()[0]
return stdout
+
+
+class MessageDialogRunner(object):
+ """
+ Create a MessageDialog, run it, destroy it and return response
+ """
+ def __init__(self, title, message,
+ type=gtk.MESSAGE_ERROR,
+ buttons=gtk.BUTTONS_OK,
+ flags=gtk.DIALOG_MODAL):
+ """
+ Create MessageDialog object
+ """
+ self.title = title
+ self.message = message
+ self.type = type
+ self.buttons = buttons
+ self.flags = flags
+
+
+ def run(self):
+ """
+ Run dialog, destroy it and return response
+ """
+ dialog = gtk.MessageDialog(type=self.type,
+ buttons=self.buttons,
+ flags=self.flags)
+ dialog.set_title(self.title)
+ dialog.set_markup(self.message)
+ response = dialog.run()
+ dialog.destroy()
+ return response
=== modified file 'checkbox_editor/vc.py'
--- checkbox_editor/vc.py 2010-06-03 08:49:41 +0000
+++ checkbox_editor/vc.py 2010-09-02 16:41:06 +0000
@@ -5,7 +5,7 @@
pygtk.require("2.0")
import gtk
-import os, logging, threading
+import os, logging, threading, shutil
from cStringIO import StringIO
from contextlib import contextmanager
@@ -17,7 +17,7 @@
import bzrlib.plugin
bzrlib.plugin.load_plugins()
-from .util import handle_exceptions
+from .util import handle_exceptions, MessageDialogRunner
class VersionControl:
@@ -209,14 +209,9 @@
Push changes
"""
if not branch_url:
- dialog = gtk.MessageDialog(type = gtk.MESSAGE_ERROR,
- flags = gtk.DIALOG_MODAL,
- buttons = gtk.BUTTONS_OK)
- dialog.set_title('Error')
- dialog.set_markup('No push branch defined in preferences.')
- dialog.show_all()
- dialog.run()
- dialog.destroy()
+ MessageDialogRunner('Error',
+ 'Push branch <b>not</b> defined '
+ 'in preferences.').run()
return False
logging.debug('Pushing changes to: {0}'.format(repr(branch_url)))
@@ -243,14 +238,9 @@
Pull changes
"""
if not branch_url:
- dialog = gtk.MessageDialog(type = gtk.MESSAGE_ERROR,
- flags = gtk.DIALOG_MODAL,
- buttons = gtk.BUTTONS_OK)
- dialog.set_title('Error')
- dialog.set_markup('No pull branch defined in preferences.')
- dialog.show_all()
- dialog.run()
- dialog.destroy()
+ MessageDialogRunner('Error',
+ 'Pull branch <b>not</b> defined '
+ 'in preferences.').run()
return False
logging.debug('Pulling changes from: {0}'.format(repr(branch_url)))
@@ -356,6 +346,15 @@
dialog.destroy()
+ def clone(self, destination_directory):
+ """
+ Clone working directory to destination
+ """
+ logging.debug('Cloning bzr repository from {0!r} to {1!r}'
+ .format(self.directory, destination_directory))
+ self.working_tree.bzrdir.sprout(destination_directory)
+
+
class NullVC(VersionControl):
"""
No version control at all, just files
@@ -393,3 +392,23 @@
Rename file in the file system
"""
os.rename(old_name, new_name)
+
+
+ def clone(self, destination_directory):
+ """
+ Copy all files to a remote directory
+ """
+ # Directory must not exist for copytree to work
+ # but FileChooserDialog creates it, so here it's
+ # removed before trying to copy the contents
+ # of the original directory
+ os.rmdir(destination_directory)
+
+ # Copy all files in the old shared directory to the new one
+ source_directory = self.directory
+ logging.debug('Copying files from {0!r} to {1!r}'
+ .format(source_directory, destination_directory))
+ shutil.copytree(source_directory, destination_directory, symlinks=True)
+
+
+5175
=== modified file 'debian/changelog'
--- debian/changelog 2010-08-31 10:22:18 +0000
+++ debian/changelog 2010-09-02 16:41:06 +0000
@@ -1,3 +1,10 @@
+checkbox-editor (0.9-0ubuntu1~ppa43) lucid; urgency=low
+
+ * Message dialog warns user on shared directory insufficient permissions (LP: #619720)
+ * 'Save as' functionality implemented (LP: #619720)
+
+ -- Javier Collado <javier.collado@xxxxxxxxxxxxx> Thu, 02 Sep 2010 18:29:13 +0200
+
checkbox-editor (0.9-0ubuntu1~ppa42) lucid; urgency=low
* Added tooltips to toolbuttons (LP: #619710)
Follow ups