Example #1
0
	def __init__(self, parent):
		WizardPage.__init__(self, parent, pgid.FILES)

		# *** Left Panel *** #

		pnl_treeopts = BorderedPanel(self)

		self.chk_individuals = CheckBoxCFG(pnl_treeopts, label=GT(u'List files individually'),
				name=u'individually', cfgSect=u'FILES')

		self.chk_preserve_top = CheckBoxCFG(pnl_treeopts, chkid.TOPLEVEL, GT(u'Preserve top-level directories'),
				name=u'top-level', cfgSect=u'FILES')

		self.chk_nofollow_symlink = CheckBoxCFG(pnl_treeopts, chkid.SYMLINK, GT(u'Don\'t follow symbolic links'),
				defaultValue=True, name=u'nofollow-symlink', cfgSect=u'FILES')

		self.tree_dirs = DirectoryTreePanel(self, size=(300,20))

		# ----- Target path
		pnl_target = BorderedPanel(self)

		# choices of destination
		rb_bin = wx.RadioButton(pnl_target, label=u'/bin', style=wx.RB_GROUP)
		rb_usrbin = wx.RadioButton(pnl_target, label=u'/usr/bin')
		rb_usrlib = wx.RadioButton(pnl_target, label=u'/usr/lib')
		rb_locbin = wx.RadioButton(pnl_target, label=u'/usr/local/bin')
		rb_loclib = wx.RadioButton(pnl_target, label=u'/usr/local/lib')
		self.rb_custom = wx.RadioButton(pnl_target, inputid.CUSTOM, GT(u'Custom'))
		self.rb_custom.Default = True

		# Start with "Custom" selected
		self.rb_custom.SetValue(self.rb_custom.Default)

		# group buttons together
		# FIXME: Unnecessary???
		self.grp_targets = (
			rb_bin,
			rb_usrbin,
			rb_usrlib,
			rb_locbin,
			rb_loclib,
			self.rb_custom,
			)

		# ----- Add/Remove/Clear buttons
		btn_add = CreateButton(self, btnid.ADD)
		btn_remove = CreateButton(self, btnid.REMOVE)
		btn_clear = CreateButton(self, btnid.CLEAR)

		self.prev_dest_value = u'/usr/bin'
		self.ti_target = TextArea(self, defaultValue=self.prev_dest_value, name=u'target')

		self.btn_browse = CreateButton(self, btnid.BROWSE)
		btn_refresh = CreateButton(self, btnid.REFRESH)

		# Display area for files added to list
		self.lst_files = FileListESS(self, inputid.LIST, name=u'filelist')

		# *** Event Handling *** #

		# create an event to enable/disable custom widget
		for item in self.grp_targets:
			wx.EVT_RADIOBUTTON(item, wx.ID_ANY, self.OnSetDestination)

		# Context menu events for directory tree
		wx.EVT_MENU(self, wx.ID_ADD, self.OnImportFromTree)

		# Button events
		btn_add.Bind(wx.EVT_BUTTON, self.OnImportFromTree)
		btn_remove.Bind(wx.EVT_BUTTON, self.OnRemoveSelected)
		btn_clear.Bind(wx.EVT_BUTTON, self.OnClearFileList)
		self.btn_browse.Bind(wx.EVT_BUTTON, self.OnBrowse)
		btn_refresh.Bind(wx.EVT_BUTTON, self.OnRefreshFileList)

		# ???: Not sure what these do
		wx.EVT_KEY_DOWN(self.ti_target, self.GetDestValue)
		wx.EVT_KEY_UP(self.ti_target, self.CheckDest)

		# Key events for file list
		wx.EVT_KEY_DOWN(self.lst_files, self.OnRemoveSelected)

		self.Bind(wx.EVT_DROP_FILES, self.OnDropFiles)

		# *** Layout *** #

		lyt_treeopts = BoxSizer(wx.VERTICAL)
		lyt_treeopts.AddSpacer(5)
		lyt_treeopts.Add(self.chk_individuals, 0, lyt.PAD_LR, 5)
		lyt_treeopts.Add(self.chk_preserve_top, 0, lyt.PAD_LR, 5)
		lyt_treeopts.Add(self.chk_nofollow_symlink, 0, lyt.PAD_LR, 5)
		lyt_treeopts.AddSpacer(5)

		pnl_treeopts.SetSizer(lyt_treeopts)

		lyt_left = BoxSizer(wx.VERTICAL)
		lyt_left.AddSpacer(10)
		lyt_left.Add(wx.StaticText(self, label=GT(u'Directory options')), 0, wx.ALIGN_BOTTOM)
		lyt_left.Add(pnl_treeopts, 0, wx.EXPAND|wx.ALIGN_LEFT|wx.BOTTOM, 5)
		lyt_left.Add(self.tree_dirs, 1, wx.EXPAND)

		lyt_target = wx.GridSizer(3, 2, 5, 5)

		for item in self.grp_targets:
			lyt_target.Add(item, 0, lyt.PAD_LR, 5)

		pnl_target.SetAutoLayout(True)
		pnl_target.SetSizer(lyt_target)
		pnl_target.Layout()

		# Put text input in its own sizer to force expand
		lyt_input = BoxSizer(wx.HORIZONTAL)
		lyt_input.Add(self.ti_target, 1, wx.ALIGN_CENTER_VERTICAL)

		lyt_buttons = BoxSizer(wx.HORIZONTAL)
		lyt_buttons.Add(btn_add, 0)
		lyt_buttons.Add(btn_remove, 0)
		lyt_buttons.Add(btn_clear, 0)
		lyt_buttons.Add(lyt_input, 1, wx.ALIGN_CENTER_VERTICAL)
		lyt_buttons.Add(self.btn_browse, 0)
		lyt_buttons.Add(btn_refresh, 0)

		lyt_right = BoxSizer(wx.VERTICAL)
		lyt_right.AddSpacer(10)
		lyt_right.Add(wx.StaticText(self, label=GT(u'Target')))
		lyt_right.Add(pnl_target, 0, wx.TOP, 5)
		lyt_right.Add(lyt_buttons, 0, wx.EXPAND)
		lyt_right.Add(self.lst_files, 5, wx.EXPAND|wx.TOP, 5)

		PROP_LEFT = 0
		PROP_RIGHT = 1

		lyt_main = wx.FlexGridSizer(1, 2)
		lyt_main.AddGrowableRow(0)

		# Directory tree size issues with wx 2.8
		if wx.MAJOR_VERSION <= 2:
			PROP_LEFT = 1
			lyt_main.AddGrowableCol(0, 1)

		lyt_main.AddGrowableCol(1, 2)
		lyt_main.Add(lyt_left, PROP_LEFT, wx.EXPAND|lyt.PAD_LR|wx.BOTTOM, 5)
		lyt_main.Add(lyt_right, PROP_RIGHT, wx.EXPAND|lyt.PAD_RB, 5)

		self.SetAutoLayout(True)
		self.SetSizer(lyt_main)
		self.Layout()

		SetPageToolTips(self)
Example #2
0
class Page(WizardPage):
	## Constructor
	#
	#  \param parent
	#	Parent <b><i>wx.Window</i></b> instance
	def __init__(self, parent):
		WizardPage.__init__(self, parent, pgid.FILES)

		# *** Left Panel *** #

		pnl_treeopts = BorderedPanel(self)

		self.chk_individuals = CheckBoxCFG(pnl_treeopts, label=GT(u'List files individually'),
				name=u'individually', cfgSect=u'FILES')

		self.chk_preserve_top = CheckBoxCFG(pnl_treeopts, chkid.TOPLEVEL, GT(u'Preserve top-level directories'),
				name=u'top-level', cfgSect=u'FILES')

		self.chk_nofollow_symlink = CheckBoxCFG(pnl_treeopts, chkid.SYMLINK, GT(u'Don\'t follow symbolic links'),
				defaultValue=True, name=u'nofollow-symlink', cfgSect=u'FILES')

		self.tree_dirs = DirectoryTreePanel(self, size=(300,20))

		# ----- Target path
		pnl_target = BorderedPanel(self)

		# choices of destination
		rb_bin = wx.RadioButton(pnl_target, label=u'/bin', style=wx.RB_GROUP)
		rb_usrbin = wx.RadioButton(pnl_target, label=u'/usr/bin')
		rb_usrlib = wx.RadioButton(pnl_target, label=u'/usr/lib')
		rb_locbin = wx.RadioButton(pnl_target, label=u'/usr/local/bin')
		rb_loclib = wx.RadioButton(pnl_target, label=u'/usr/local/lib')
		self.rb_custom = wx.RadioButton(pnl_target, inputid.CUSTOM, GT(u'Custom'))
		self.rb_custom.Default = True

		# Start with "Custom" selected
		self.rb_custom.SetValue(self.rb_custom.Default)

		# group buttons together
		# FIXME: Unnecessary???
		self.grp_targets = (
			rb_bin,
			rb_usrbin,
			rb_usrlib,
			rb_locbin,
			rb_loclib,
			self.rb_custom,
			)

		# ----- Add/Remove/Clear buttons
		btn_add = CreateButton(self, btnid.ADD)
		btn_remove = CreateButton(self, btnid.REMOVE)
		btn_clear = CreateButton(self, btnid.CLEAR)

		self.prev_dest_value = u'/usr/bin'
		self.ti_target = TextArea(self, defaultValue=self.prev_dest_value, name=u'target')

		self.btn_browse = CreateButton(self, btnid.BROWSE)
		btn_refresh = CreateButton(self, btnid.REFRESH)

		# Display area for files added to list
		self.lst_files = FileListESS(self, inputid.LIST, name=u'filelist')

		# *** Event Handling *** #

		# create an event to enable/disable custom widget
		for item in self.grp_targets:
			wx.EVT_RADIOBUTTON(item, wx.ID_ANY, self.OnSetDestination)

		# Context menu events for directory tree
		wx.EVT_MENU(self, wx.ID_ADD, self.OnImportFromTree)

		# Button events
		btn_add.Bind(wx.EVT_BUTTON, self.OnImportFromTree)
		btn_remove.Bind(wx.EVT_BUTTON, self.OnRemoveSelected)
		btn_clear.Bind(wx.EVT_BUTTON, self.OnClearFileList)
		self.btn_browse.Bind(wx.EVT_BUTTON, self.OnBrowse)
		btn_refresh.Bind(wx.EVT_BUTTON, self.OnRefreshFileList)

		# ???: Not sure what these do
		wx.EVT_KEY_DOWN(self.ti_target, self.GetDestValue)
		wx.EVT_KEY_UP(self.ti_target, self.CheckDest)

		# Key events for file list
		wx.EVT_KEY_DOWN(self.lst_files, self.OnRemoveSelected)

		self.Bind(wx.EVT_DROP_FILES, self.OnDropFiles)

		# *** Layout *** #

		lyt_treeopts = BoxSizer(wx.VERTICAL)
		lyt_treeopts.AddSpacer(5)
		lyt_treeopts.Add(self.chk_individuals, 0, lyt.PAD_LR, 5)
		lyt_treeopts.Add(self.chk_preserve_top, 0, lyt.PAD_LR, 5)
		lyt_treeopts.Add(self.chk_nofollow_symlink, 0, lyt.PAD_LR, 5)
		lyt_treeopts.AddSpacer(5)

		pnl_treeopts.SetSizer(lyt_treeopts)

		lyt_left = BoxSizer(wx.VERTICAL)
		lyt_left.AddSpacer(10)
		lyt_left.Add(wx.StaticText(self, label=GT(u'Directory options')), 0, wx.ALIGN_BOTTOM)
		lyt_left.Add(pnl_treeopts, 0, wx.EXPAND|wx.ALIGN_LEFT|wx.BOTTOM, 5)
		lyt_left.Add(self.tree_dirs, 1, wx.EXPAND)

		lyt_target = wx.GridSizer(3, 2, 5, 5)

		for item in self.grp_targets:
			lyt_target.Add(item, 0, lyt.PAD_LR, 5)

		pnl_target.SetAutoLayout(True)
		pnl_target.SetSizer(lyt_target)
		pnl_target.Layout()

		# Put text input in its own sizer to force expand
		lyt_input = BoxSizer(wx.HORIZONTAL)
		lyt_input.Add(self.ti_target, 1, wx.ALIGN_CENTER_VERTICAL)

		lyt_buttons = BoxSizer(wx.HORIZONTAL)
		lyt_buttons.Add(btn_add, 0)
		lyt_buttons.Add(btn_remove, 0)
		lyt_buttons.Add(btn_clear, 0)
		lyt_buttons.Add(lyt_input, 1, wx.ALIGN_CENTER_VERTICAL)
		lyt_buttons.Add(self.btn_browse, 0)
		lyt_buttons.Add(btn_refresh, 0)

		lyt_right = BoxSizer(wx.VERTICAL)
		lyt_right.AddSpacer(10)
		lyt_right.Add(wx.StaticText(self, label=GT(u'Target')))
		lyt_right.Add(pnl_target, 0, wx.TOP, 5)
		lyt_right.Add(lyt_buttons, 0, wx.EXPAND)
		lyt_right.Add(self.lst_files, 5, wx.EXPAND|wx.TOP, 5)

		PROP_LEFT = 0
		PROP_RIGHT = 1

		lyt_main = wx.FlexGridSizer(1, 2)
		lyt_main.AddGrowableRow(0)

		# Directory tree size issues with wx 2.8
		if wx.MAJOR_VERSION <= 2:
			PROP_LEFT = 1
			lyt_main.AddGrowableCol(0, 1)

		lyt_main.AddGrowableCol(1, 2)
		lyt_main.Add(lyt_left, PROP_LEFT, wx.EXPAND|lyt.PAD_LR|wx.BOTTOM, 5)
		lyt_main.Add(lyt_right, PROP_RIGHT, wx.EXPAND|lyt.PAD_RB, 5)

		self.SetAutoLayout(True)
		self.SetSizer(lyt_main)
		self.Layout()

		SetPageToolTips(self)


	## Adds files to file list
	#
	#  \param dirs
	#	<b><i>dict</i></b>: dict[dir] = [file list]
	#  \param fileCount
	#	Number of explicit files being added to list
	#  \param showDialog
	#	If <b><i>True</i></b>, displays a progress dialog
	def AddPaths(self, dirs, fileCount=None, showDialog=False):
		target = self.GetTarget()

		if fileCount == None:
			fileCount = 0
			for D in dirs:
				for F in dirs[D]:
					fileCount += 1

		progress = None

		Logger.Debug(__name__, u'Adding {} files ...'.format(fileCount))

		if showDialog:
			progress = ProgressDialog(GetMainWindow(), GT(u'Adding Files'), maximum=fileCount,
					style=PD_DEFAULT_STYLE|wx.PD_CAN_ABORT)
			progress.Show()

		completed = 0
		for D in sorted(dirs):
			for F in sorted(dirs[D]):
				if progress and progress.WasCancelled():
					progress.Destroy()
					return False

				if progress:
					wx.Yield()
					progress.Update(completed, GT(u'Adding file {}').format(F))

				self.lst_files.AddFile(F, D, target)

				completed += 1

		if progress:
			wx.Yield()
			progress.Update(completed)

			progress.Destroy()

		return True


	## TODO: Doxygen
	def CheckDest(self, event=None):
		if TextIsEmpty(self.ti_target.GetValue()):
			self.ti_target.SetValue(self.prev_dest_value)
			self.ti_target.SetInsertionPoint(-1)

		elif self.ti_target.GetValue()[0] != u'/':
			self.ti_target.SetValue(self.prev_dest_value)
			self.ti_target.SetInsertionPoint(-1)

		if event:
			event.Skip()


	## Retrieves information on files to be packaged
	#
	#  \return
	#	A list of files with their targets formatted for text output
	def Get(self):
		# Remove section delimeters & first line which is just an integer
		return self.GetSaveData().split(u'\n')[2:-1]


	## Retrieves target destination set by user input
	#
	#  TODO: Rename to 'GetTarget' or 'GetInputTarget'
	def GetDestValue(self, event=None):
		if not TextIsEmpty(self.ti_target.GetValue()):
			if self.ti_target.GetValue()[0] == u'/':
				self.prev_dest_value = self.ti_target.GetValue()

		if event:
			event.Skip()


	## Retrieves the directory tree object used by this page
	#
	#  Used in input.list.FileList for referencing size
	#
	#  \return
	#	<b><i>ui.tree.DirectoryTreePanel</i></b> instance
	def GetDirTreePanel(self):
		return self.tree_dirs


	## Retrieves number of files in list
	#
	#  \return
	#	<b><i>Integer</i></b> count of items in file list
	def GetFileCount(self):
		return self.lst_files.GetItemCount()


	## Retrieves the file list object used by this page
	#
	#  \return
	#	<b><i>input.list.FileList</i></b> instance
	def GetListInstance(self):
		return self.lst_files


	## Retrieves file list to export to text file
	#
	#  \return
	#	List formatted text
	def GetSaveData(self):
		file_list = []
		item_count = self.lst_files.GetItemCount()

		if item_count > 0:
			count = 0
			while count < item_count:
				filename = self.lst_files.GetItemText(count)
				source = self.lst_files.GetItem(count, columns.SOURCE).GetText()
				target = self.lst_files.GetItem(count, columns.TARGET).GetText()
				absolute_filename = ConcatPaths((source, filename))

				# Populate list with tuples of ('src', 'file', 'dest')
				if self.lst_files.GetItemTextColour(count) == (255, 0, 0):
					# Mark file as executable
					file_list.append((u'{}*'.format(absolute_filename), filename, target))

				else:
					file_list.append((absolute_filename, filename, target))

				count += 1

			return_list = []
			for F in file_list:
				f0 = u'{}'.encode(u'utf-8').format(F[0])
				f1 = u'{}'.encode(u'utf-8').format(F[1])
				f2 = u'{}'.encode(u'utf-8').format(F[2])
				return_list.append(u'{} -> {} -> {}'.format(f0, f1, f2))

			return u'<<FILES>>\n1\n{}\n<</FILES>>'.format(u'\n'.join(return_list))

		else:
			# Place a "0" in FILES field if we are not saving any files
			return u'<<FILES>>\n0\n<</FILES>>'


	## Retrieves the target output directory
	#
	#  FIXME: Duplicate of wizbin.files.Page.GetDestValue?
	def GetTarget(self):
		if FieldEnabled(self.ti_target):
			return self.ti_target.GetValue()

		for target in self.grp_targets:
			if target.GetId() != inputid.CUSTOM and target.GetValue():
				return target.GetLabel()


	## Accepts a file path to read & parse to fill the page's fields
	#
	#  \param filename
	#	Absolute path of formatted text file to read
	def ImportFromFile(self, filename):
		Logger.Debug(__name__, GT(u'Importing page info from {}').format(filename))

		if not os.path.isfile(filename):
			return dbrerrno.ENOENT

		files_data = ReadFile(filename, split=True)

		# Lines beginning with these characters will be ignored
		ignore_characters = (
			u'',
			u' ',
			u'#',
		)

		target = None
		targets_list = []

		for L in files_data:
			if not TextIsEmpty(L) and L[0] not in ignore_characters:
				if u'[' in L and u']' in L:
					target = L.split(u'[')[-1].split(u']')[0]
					continue

				if target:
					executable = (len(L) > 1 and L[-2:] == u' *')
					if executable:
						L = L[:-2]

					targets_list.append((target, L, executable))

		missing_files = []

		for T in targets_list:
			# FIXME: Create method in FileList class to retrieve all missing files
			if not os.path.exists(T[1]):
				missing_files.append(T[1])

			source_file = os.path.basename(T[1])
			source_dir = os.path.dirname(T[1])

			self.lst_files.AddFile(source_file, source_dir, T[0], executable=T[2])

		if len(missing_files):
			main_window = GetMainWindow()

			err_line1 = GT(u'The following files/folders are missing from the filesystem.')
			err_line2 = GT(u'They will be highlighted on the Files page.')
			DetailedMessageDialog(main_window, title=GT(u'Warning'), icon=ICON_ERROR,
					text=u'\n'.join((err_line1, err_line2)),
					details=u'\n'.join(missing_files)).ShowModal()

		return 0


	## Checks if the page is ready for export/build
	#
	#  \return
	#	<b><i>True</i></b> if the file list (self.lst_files) is not empty
	def IsOkay(self):
		return not self.lst_files.IsEmpty()


	## Reads files & directories & preps for loading into list
	#
	#  \param pathsList
	#	<b><i>List/Tuple</i></b> of <b><i>string</i></b> values representing
	#	files & directories to be added
	#  \return
	#	Value of wizbin.files.Page.AddPaths, or <b><i>False</i></b> in case of error
	def LoadPaths(self, pathsList):
		if isinstance(pathsList, tuple):
			pathsList = list(pathsList)

		if not pathsList or not isinstance(pathsList, list):
			return False

		file_list = []
		dir_list = {}

		prep = ProgressDialog(GetMainWindow(), GT(u'Processing Files'), GT(u'Scanning files ...'),
				style=wx.PD_APP_MODAL|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT)

		# Only update the gauge every N files (hack until I figure out timer)
		update_interval = 450
		count = 0

		prep.Show()

		if not self.chk_preserve_top.GetValue():
			for INDEX in reversed(range(len(pathsList))):
				path = pathsList[INDEX]
				if os.path.isdir(path):
					# Remove top-level directory from list
					pathsList.pop(INDEX)

					insert_index = INDEX
					for P in os.listdir(path):
						pathsList.insert(insert_index, ConcatPaths((path, P)))
						insert_index += 1

		try:
			for P in pathsList:
				if prep.WasCancelled():
					prep.Destroy()
					return False

				count += 1
				if count >= update_interval:
					wx.Yield()
					prep.Pulse()
					count = 0

				if not self.chk_individuals.GetValue() or os.path.isfile(P):
					file_list.append(P)
					continue

				if os.path.isdir(P):
					parent_dir = os.path.dirname(P)

					if parent_dir not in dir_list:
						dir_list[parent_dir] = []

					for ROOT, DIRS, FILES in os.walk(P):
						if prep.WasCancelled():
							prep.Destroy()
							return False

						wx.Yield()
						prep.SetMessage(GT(u'Scanning directory {} ...').format(ROOT))

						count += 1
						if count >= update_interval:
							wx.Yield()
							prep.Pulse()
							count = 0

						for F in FILES:
							if prep.WasCancelled():
								prep.Destroy()
								return False

							count += 1
							if count >= update_interval:
								wx.Yield()
								prep.Pulse()
								count = 0

							# os.path.dirname preserves top level directory
							ROOT = ROOT.replace(os.path.dirname(P), u'').strip(u'/')
							F = u'{}/{}'.format(ROOT, F).strip(u'/')

							if F not in dir_list[parent_dir]:
								dir_list[parent_dir].append(F)

		except:
			prep.Destroy()

			ShowErrorDialog(GT(u'Could not retrieve file list'), traceback.format_exc())

			return False

		wx.Yield()
		prep.Pulse(GT(u'Counting Files'))

		file_count = len(file_list)

		count = 0
		for D in dir_list:
			for F in dir_list[D]:
				file_count += 1

				count += 1
				if count >= update_interval:
					wx.Yield()
					prep.Pulse()
					count = 0

		prep.Destroy()

		# Add files to directory list
		for F in file_list:
			f_name = os.path.basename(F)
			f_dir = os.path.dirname(F)

			if f_dir not in dir_list:
				dir_list[f_dir] = []

			dir_list[f_dir].append(f_name)

		if file_count > warning_threshhold:
			count_warnmsg = GT(u'Importing {} files'.format(file_count))
			count_warnmsg = u'{}. {}.'.format(count_warnmsg, GT(u'This could take a VERY long time'))
			count_warnmsg = u'{}\n{}'.format(count_warnmsg, GT(u'Are you sure you want to continue?'))

			if not ConfirmationDialog(GetMainWindow(), text=count_warnmsg).Confirmed():
				return False

		return self.AddPaths(dir_list, file_count, showDialog=file_count >= efficiency_threshold)


	## Handles event emitted by 'browse' button
	#
	#  Opens a directory dialog to select a custom output target
	def OnBrowse(self, event=None):
		dia = GetDirDialog(GetMainWindow(), GT(u'Choose Target Directory'))
		if ShowDialog(dia):
			self.ti_target.SetValue(dia.GetPath())


	## Handles event emitted by 'clear' button
	#
	#  Displays confirmation dialog to clear list if not empty
	#
	#  TODO: Rename to OnClearList?
	def OnClearFileList(self, event=None):
		if self.lst_files.GetItemCount():
			if ConfirmationDialog(GetMainWindow(), GT(u'Confirm'),
						GT(u'Clear all files?')).Confirmed():
				self.lst_files.DeleteAllItems()


	## Adds files to list from file manager drop
	#
	#  Note that this method should not be renamed as 'OnDropFiles'
	#  is the implicit handler for wx.FileDropTarget (<- correct class???)
	#
	#  \param fileList
	#	<b><i>List</i></b> of files dropped from file manager
	#  \return
	#	Value of wizbin.files.Page.LoadPaths
	def OnDropFiles(self, fileList):
		return self.LoadPaths(fileList)


	## Handles files & directories added from ui.tree.DirectoryTreePanel object
	#  (self.tree_dirs)
	#
	#  Actually bypasses DirectoryTreePanel & directly accesses
	#  ui.tree.DirectoryTree.GetSelectedPaths
	def OnImportFromTree(self, event=None):
		return self.LoadPaths(self.DirTree.GetSelectedPaths())


	## Updates files' status in the file list
	#
	#  Refreshes files' executable & available status
	#
	#  \return
	#	Value of self.lst_files.RefreshFileList
	def OnRefreshFileList(self, event=None):
		return self.lst_files.RefreshFileList()


	## Handles event emitted by 'remove' button
	#
	#  Removes all currently selected/highlighted files in list
	def OnRemoveSelected(self, event=None):
		try:
			modifier = event.GetModifiers()
			keycode = event.GetKeyCode()

		except AttributeError:
			keycode = event.GetEventObject().GetId()

		if keycode in (wx.ID_REMOVE, wx.WXK_DELETE):
			self.lst_files.RemoveSelected()

		elif keycode == 65 and modifier == wx.MOD_CONTROL:
			self.lst_files.SelectAll()


	## Handles enabling/disabling the custom target field if the corresponding
	#  when a target radio button is selected
	#
	#  TODO: Rename to 'OnSetTarget' or 'OnSelectTarget'
	def OnSetDestination(self, event=None):
		enable = self.rb_custom.GetValue()

		self.ti_target.Enable(enable)
		self.btn_browse.Enable(enable)


	## Resets page's fields to default values
	#
	#  \return
	#	Value of self.lst_files.Reset
	def Reset(self):
		return self.lst_files.Reset()


	## Selects all files in the list
	#
	#  \return
	#	Value of self.lst_files.SelectAll
	def SelectAll(self):
		return self.lst_files.SelectAll()


	## Sets the page's fields
	#
	#  \param data
	#	The text information to parse
	#  \return
	#	<b><i>True</i></b> if the data was imported correctly
	def Set(self, data):
		# Clear files list
		self.lst_files.DeleteAllItems()
		files_data = data.split(u'\n')
		if int(files_data[0]):
			# Get file count from list minus first item "1"
			files_total = len(files_data)

			# Store missing files here
			missing_files = []

			progress = None

			if files_total >= efficiency_threshold:
				progress = ProgressDialog(GetMainWindow(), GT(u'Adding Files'), maximum=files_total,
						style=PD_DEFAULT_STYLE|wx.PD_CAN_ABORT)

				wx.Yield()
				progress.Show()

			current_file = files_total
			while current_file > 1:
				if progress and progress.WasCancelled():
					progress.Destroy()

					# Project continues opening even if file import is cancelled
					msg = (
						GT(u'File import did not complete.'),
						GT(u'Project files may be missing in file list.'),
						)

					ShowMessageDialog(u'\n'.join(msg), GT(u'Import Cancelled'))

					return False

				current_file -= 1
				executable = False

				file_info = files_data[current_file].split(u' -> ')
				absolute_filename = file_info[0]

				if absolute_filename[-1] == u'*':
					# Set executable flag and remove "*"
					executable = True
					absolute_filename = absolute_filename[:-1]

				filename = file_info[1]
				source_dir = absolute_filename[:len(absolute_filename) - len(filename)]
				target_dir = file_info[2]

				if not self.lst_files.AddFile(filename, source_dir, target_dir, executable):
					Logger.Warn(__name__, GT(u'File not found: {}').format(absolute_filename))
					missing_files.append(absolute_filename)

				if progress:
					update_value = files_total - current_file

					wx.Yield()
					progress.Update(update_value+1, GT(u'Imported file {} of {}').format(update_value, files_total))

			if progress:
				progress.Destroy()

			Logger.Debug(__name__, u'Missing file count: {}'.format(len(missing_files)))

			# If files are missing show a message
			if missing_files:
				alert = DetailedMessageDialog(GetMainWindow(), GT(u'Missing Files'),
						ICON_EXCLAMATION, GT(u'Could not locate the following files:'),
						u'\n'.join(missing_files))
				alert.ShowModal()

			return True
Example #3
0
class Page(WizardPage):
    ## Constructor
    #
    #  \param parent
    #	Parent <b><i>wx.Window</i></b> instance
    def __init__(self, parent):
        WizardPage.__init__(self, parent, pgid.BUILD)

        # Bypass build prep check
        self.prebuild_check = False

        # Add checkable items to this list
        # FIXME: Use a different method
        self.build_options = []

        # ----- Extra Options

        pnl_options = BorderedPanel(self)

        self.chk_md5 = CheckBoxESS(pnl_options,
                                   chkid.MD5,
                                   GT(u'Create md5sums file'),
                                   name=u'MD5',
                                   defaultValue=True,
                                   commands=u'md5sum')
        # The » character denotes that an alternate tooltip should be shown if the control is disabled
        self.chk_md5.tt_name = u'md5»'
        self.chk_md5.col = 0

        if UsingTest(u'alpha'):
            # Brings up control file preview for editing
            self.chk_editctrl = CheckBoxCFG(
                pnl_options,
                chkid.EDIT,
                GT(u'Preview control file for editing'),
                name=u'editctrl')
            self.chk_editctrl.col = 1

        # TODO: Use CheckBoxCFG instead of CheckBoxESS:
        #		   Fields will be set from config instead of project file

        # Option to strip binaries
        self.chk_strip = CheckBoxESS(pnl_options,
                                     chkid.STRIP,
                                     GT(u'Strip binaries'),
                                     name=u'strip»',
                                     defaultValue=True,
                                     commands=u'strip')
        self.chk_strip.col = 0

        # Deletes the temporary build tree
        self.chk_rmstage = CheckBoxESS(pnl_options,
                                       chkid.DELETE,
                                       GT(u'Delete staged directory'),
                                       name=u'RMSTAGE',
                                       defaultValue=True)
        self.chk_rmstage.col = 0

        # Checks the output .deb for errors
        self.chk_lint = CheckBoxESS(
            pnl_options,
            chkid.LINT,
            GT(u'Check package for errors with lintian'),
            name=u'LINTIAN',
            defaultValue=True,
            commands=u'lintian')
        self.chk_lint.tt_name = u'lintian»'
        self.chk_lint.col = 0

        # Installs the deb on the system
        self.chk_install = CheckBox(pnl_options,
                                    chkid.INSTALL,
                                    GT(u'Install package after build'),
                                    name=u'INSTALL',
                                    commands=(
                                        u'gdebi-gtk',
                                        u'gdebi-kde',
                                    ))
        self.chk_install.tt_name = u'install»'
        self.chk_install.col = 0

        # *** Lintian Overrides *** #

        if UsingTest(u'alpha'):
            # FIXME: Move next to lintian check box
            self.lint_overrides = []
            btn_lint_overrides = CreateButton(self,
                                              label=GT(u'Lintian overrides'))
            btn_lint_overrides.Bind(wx.EVT_BUTTON, self.OnSetLintOverrides)

        btn_build = CreateButton(self, btnid.BUILD, GT(u'Build'), u'build', 64)

        # Display log
        dsp_log = OutputLog(self)

        SetPageToolTips(self)

        # *** Event Handling *** #

        btn_build.Bind(wx.EVT_BUTTON, self.OnBuild)

        # *** Layout *** #

        lyt_options = wx.GridBagSizer()

        next_row = 0
        prev_row = next_row
        for CHK in pnl_options.Children:
            row = next_row
            FLAGS = lyt.PAD_LR

            if CHK.col:
                row = prev_row
                FLAGS = wx.RIGHT

            lyt_options.Add(CHK, (row, CHK.col), flag=FLAGS, border=5)

            if not CHK.col:
                prev_row = next_row
                next_row += 1

        pnl_options.SetSizer(lyt_options)
        pnl_options.SetAutoLayout(True)
        pnl_options.Layout()

        lyt_buttons = BoxSizer(wx.HORIZONTAL)
        lyt_buttons.Add(btn_build, 1)

        lyt_main = BoxSizer(wx.VERTICAL)
        lyt_main.AddSpacer(10)
        lyt_main.Add(wx.StaticText(self, label=GT(u'Extra Options')), 0,
                     lyt.ALGN_LB | wx.LEFT, 5)
        lyt_main.Add(pnl_options, 0, wx.LEFT, 5)
        lyt_main.AddSpacer(5)

        if UsingTest(u'alpha'):
            #lyt_main.Add(wx.StaticText(self, label=GT(u'Lintian overrides')), 0, wx.LEFT, 5)
            lyt_main.Add(btn_lint_overrides, 0, wx.LEFT, 5)

        lyt_main.AddSpacer(5)
        lyt_main.Add(lyt_buttons, 0, lyt.ALGN_C)
        lyt_main.Add(dsp_log, 2, wx.EXPAND | lyt.PAD_LRB, 5)

        self.SetAutoLayout(True)
        self.SetSizer(lyt_main)
        self.Layout()

        # *** Post-layout functions *** #

        self.InitDefaultSettings()

    ## Method that builds the actual Debian package
    #
    #  TODO: Test for errors when building deb package with other filename extension
    #  TODO: Remove deprecated methods that this one replaces
    #  \param outFile
    #		\b \e str|unicode : Absolute path to target file
    def Build(self, outFile):
        def log_message(msg, current_step, total_steps):
            return u'{} ({}/{})'.format(msg, current_step, total_steps)

        wizard = GetWizard()
        pages_build_ids = self.BuildPrep()

        if pages_build_ids != None:
            main_window = GetMainWindow()

            # Reported at the end of build
            build_summary = []

            steps_count = len(pages_build_ids)
            current_step = 0

            # Steps from build page
            for chk in self.chk_md5, self.chk_lint, self.chk_rmstage:
                if chk.IsChecked():
                    steps_count += 1

            # Control file & .deb build step
            steps_count += 2

            stage = CreateStage()

            log_msg = GT(u'Starting build')

            wx.YieldIfNeeded()
            # FIXME: Enable PD_CAN_ABORT
            build_progress = ProgressDialog(main_window,
                                            GT(u'Building'),
                                            log_msg,
                                            maximum=steps_count)

            build_summary.append(u'{}:'.format(log_msg))

            try:
                for P in wizard.GetAllPages():
                    if build_progress.WasCancelled():
                        break

                    if P.GetId() in pages_build_ids:
                        p_label = P.GetTitle()

                        log_msg = log_message(
                            GT(u'Processing page "{}"').format(p_label),
                            current_step + 1, steps_count)

                        # FIXME: Progress bar not updating???
                        wx.YieldIfNeeded()
                        build_progress.Update(current_step, log_msg)

                        ret_code, ret_value = P.ExportBuild(stage)

                        build_summary.append(u'\n{}:\n{}'.format(
                            log_msg, ret_value))

                        if ret_code > 0:
                            build_progress.Destroy()

                            ShowErrorDialog(GT(u'Error occurred during build'),
                                            ret_value)

                            return

                        current_step += 1

                # *** Control File *** #
                if not build_progress.WasCancelled():
                    wx.YieldIfNeeded()

                    log_msg = log_message(GT(u'Creating control file'),
                                          current_step + 1, steps_count)
                    build_progress.Update(current_step, log_msg)

                    Logger.Debug(__name__, log_msg)

                    # Retrieve control page
                    pg_control = wizard.GetPage(pgid.CONTROL)
                    if not pg_control:
                        build_progress.Destroy()

                        ShowErrorDialog(
                            GT(u'Could not retrieve control page'),
                            GT(u'Please contact the developer: {}').format(
                                AUTHOR_email),
                            title=u'Fatal Error')

                        return

                    installed_size = self.OnBuildGetInstallSize(stage)

                    Logger.Debug(
                        __name__,
                        GT(u'Installed size: {}').format(installed_size))

                    build_summary.append(u'\n{}:'.format(log_msg))
                    build_summary.append(
                        pg_control.ExportBuild(
                            u'{}/DEBIAN'.format(stage).replace(u'//', u'/'),
                            installed_size))

                    current_step += 1

                # *** MD5 Checksum *** #
                if not build_progress.WasCancelled():
                    if self.chk_md5.GetValue() and GetExecutable(u'md5sum'):
                        log_msg = log_message(GT(u'Creating MD5 checksum'),
                                              current_step + 1, steps_count)
                        #log_msg = GT(u'Creating MD5 checksum')
                        #step = u'{}/{}'.format(current_step+1, steps_count)

                        Logger.Debug(__name__, log_msg)

                        wx.YieldIfNeeded()
                        build_progress.Update(current_step, log_msg)

                        build_summary.append(u'\n{}:'.format(log_msg))
                        build_summary.append(self.OnBuildMD5Sum(stage))

                        current_step += 1

                # *** Create .deb from Stage *** #
                if not build_progress.WasCancelled():
                    log_msg = log_message(GT(u'Creating .deb package'),
                                          current_step + 1, steps_count)

                    wx.YieldIfNeeded()
                    build_progress.Update(current_step, log_msg)

                    build_summary.append(u'\n{}:'.format(log_msg))
                    build_summary.append(
                        self.OnBuildCreatePackage(stage, outFile))

                    current_step += 1

                # *** Lintian *** #
                if not build_progress.WasCancelled():
                    if self.chk_lint.IsChecked():
                        log_msg = log_message(
                            GT(u'Checking package with lintian'),
                            current_step + 1, steps_count)

                        wx.YieldIfNeeded()
                        build_progress.Update(current_step, log_msg)

                        build_summary.append(u'\n{}:'.format(log_msg))
                        build_summary.append(self.OnBuildCheckPackage(outFile))

                        current_step += 1

                # *** Delete Stage *** #
                if not build_progress.WasCancelled():
                    if self.chk_rmstage.IsChecked():
                        log_msg = log_message(
                            GT(u'Removing staged build tree'),
                            current_step + 1, steps_count)

                        wx.YieldIfNeeded()
                        build_progress.Update(current_step, log_msg)

                        build_summary.append(u'\n{}:'.format(log_msg))
                        RemoveStage(stage)

                        if not os.path.isdir(stage):
                            build_summary.append(
                                GT(u'Staged build tree removed successfully'))

                        else:
                            build_summary.append(
                                GT(u'Failed to remove staged build tree'))

                        current_step += 1

                # *** Show Completion Status *** #
                wx.YieldIfNeeded()
                build_progress.Update(steps_count, GT(u'Build completed'))

                # Show finished dialog for short moment
                time.sleep(1)

                # TODO: Add error count to build summary

                build_progress.Destroy()

                build_summary = u'\n'.join(build_summary)
                summary_dialog = DetailedMessageDialog(main_window,
                                                       GT(u'Build Summary'),
                                                       ICON_INFORMATION,
                                                       GT(u'Build completed'),
                                                       build_summary)
                summary_dialog.ShowModal()

            except:
                build_progress.Destroy()

                ShowErrorDialog(GT(u'Error occurred during build'),
                                traceback.format_exc())

        return

    ## Checks pages for export & gets a count of how many tasks need be executed
    #
    #  \return
    #	<b><i>Tuple</i></b> containing data & label for each page
    def BuildPrep(self):
        wizard = GetWizard()
        prep_ids = []

        pages = wizard.GetAllPages()

        for P in pages:
            if P.prebuild_check:
                Logger.Debug(
                    __name__,
                    GT(u'Pre-build check for page "{}"'.format(P.GetName())))
                prep_ids.append(P.GetId())

        try:
            main_window = GetMainWindow()

            # List of page IDs to process during build
            pg_build_ids = []

            steps_count = len(prep_ids)
            current_step = 0

            msg_label1 = GT(u'Prepping page "{}"')
            msg_label2 = GT(u'Step {}/{}')
            msg_label = u'{} ({})'.format(msg_label1, msg_label2)

            prep_progress = ProgressDialog(
                main_window,
                GT(u'Preparing Build'),
                msg_label2.format(current_step, steps_count),
                maximum=steps_count,
                style=PD_DEFAULT_STYLE | wx.PD_CAN_ABORT)

            for P in pages:
                if prep_progress.WasCancelled():
                    break

                p_id = P.GetId()
                p_label = P.GetTitle()

                if p_id in prep_ids:
                    Logger.Debug(
                        __name__,
                        msg_label.format(p_label, current_step + 1,
                                         steps_count))

                    wx.Yield()
                    prep_progress.Update(
                        current_step,
                        msg_label.format(p_label, current_step + 1,
                                         steps_count))

                    if P.IsOkay():
                        pg_build_ids.append(p_id)

                    current_step += 1

            if not prep_progress.WasCancelled():
                wx.Yield()
                prep_progress.Update(current_step, GT(u'Prepping finished'))

                # Show finished dialog for short period
                time.sleep(1)

            prep_progress.Destroy()

            return pg_build_ids

        except:
            prep_progress.Destroy()

            ShowErrorDialog(GT(u'Error occurred during pre-build'),
                            traceback.format_exc())

        return None

    ## Preview control file for editing
    def EditControl(self):
        pg_control = GetPage(pgid.CONTROL)

        ctrl_info = pg_control.GetCtrlInfo()

        preview = TextPreview(title=GT(u'Edit Control File'),
                              text=ctrl_info,
                              size=(600, 400),
                              readonly=False)
        AddCustomButtons(preview, (
            wx.ID_SAVE,
            wx.ID_CANCEL,
        ),
                         parent_sizer=True)

        if preview.ShowModal() == wx.ID_SAVE:
            Logger.Debug(__name__, u'Updating control information ...')

            ctrl_info = preview.GetValue()

            depends_data = pg_control.Set(ctrl_info)
            GetPage(pgid.DEPENDS).Set(depends_data)

    ## Retrieves page data from fields
    def Get(self, getModule=False):
        # 'install after build' is not exported to project for safety

        fields = {}
        omit_options = (self.chk_install, )

        for O in self.build_options:
            # Leave options out that should not be saved
            if O not in omit_options:
                fields[O.GetName()] = GS(O.GetValue())

        page = wx.EmptyString

        for F in fields:
            if page == wx.EmptyString:
                page = u'{}={}'.format(F, fields[F])

            else:
                page = u'{}\n{}={}'.format(page, F, fields[F])

        if page == wx.EmptyString:
            page = None

        if getModule:
            page = (
                __name__,
                page,
            )

        return page

    ## Reads & parses page data from a formatted text file
    def ImportFromFile(self, filename):
        if not os.path.isfile(filename):
            return dbrerrno.ENOENT

        build_data = ReadFile(filename, split=True)

        options_definitions = {}

        for L in build_data:
            if u'=' in L:
                key = L.split(u'=')
                value = GetBoolean(key[-1])
                key = key[0]

                options_definitions[key] = value

        for O in self.build_options:
            name = O.GetName()
            if name in options_definitions and isinstance(
                    options_definitions[name], bool):
                O.SetValue(options_definitions[name])

        return 0

    ## Sets up page with default settings
    #
    #  FIXME: Deprecated/Replace with 'Reset' method???
    def InitDefaultSettings(self):
        self.build_options = []

        option_list = (
            (
                self.chk_md5,
                GetExecutable(u'md5sum'),
            ),
            (
                self.chk_strip,
                GetExecutable(u'strip'),
            ),
            (
                self.chk_rmstage,
                True,
            ),
            (
                self.chk_lint,
                GetExecutable(u'lintian'),
            ),
            (
                self.chk_install,
                GetSystemInstaller(),
            ),
        )

        for option, command in option_list:
            # FIXME: Commands should be updated globally
            if not isinstance(command, bool):
                command = CommandExists(command)

            option.Enable(bool(command))
            option.SetValue(FieldEnabled(option) and option.Default)

            if bool(command):
                self.build_options.append(option)

    ## Installs the built .deb package onto the system
    #
    #  Uses the system's package installer: gdebi-gtk or gdebi-kde if available
    #
    #  Shows a success dialog if installed. Otherwise shows an
    #  error dialog.
    #
    #  \param package
    #	Path of package to be installed
    def InstallPackage(self, package):
        system_installer = GetSystemInstaller()

        if not system_installer:
            ShowErrorDialog(
                GT(u'Cannot install package'),
                GT(u'A compatible package manager could not be found on the system'
                   ),
                __name__,
                warn=True)

            return

        Logger.Info(__name__,
                    GT(u'Attempting to install package: {}').format(package))
        Logger.Info(__name__,
                    GT(u'Installing with {}').format(system_installer))

        install_cmd = (
            system_installer,
            package,
        )

        wx.Yield()
        # FIXME: Use ExecuteCommand here
        install_output = subprocess.Popen(install_cmd)

        # Command appears to not have been executed correctly
        if install_output == None:
            ShowErrorDialog(GT(u'Could not install package: {}'),
                            GT(u'An unknown error occurred'), __name__)

            return

        # Command executed but did not return success code
        if install_output.returncode:
            err_details = (
                GT(u'Process returned code {}').format(
                    install_output.returncode),
                GT(u'Command executed: {}').format(u' '.join(install_cmd)),
            )

            ShowErrorDialog(GT(u'An error occurred during installation'),
                            u'\n'.join(err_details), __name__)

            return

    ## Handles event emitted by the 'build' button
    #
    #  Checks if required fields are filled & opens a file save dialog
    def OnBuild(self, event=None):
        if event:
            event.Skip()

        # Show control file preview for editing
        if UsingTest(u'alpha') and self.chk_editctrl.GetValue():
            self.EditControl()

        wizard = GetWizard()

        pg_control = wizard.GetPage(pgid.CONTROL)
        pg_files = wizard.GetPage(pgid.FILES)
        pg_launcher = wizard.GetPage(pgid.LAUNCHERS)

        required_fields = {
            GT(u'Control'): pg_control.GetRequiredFields(),
        }

        # Check if launchers are enabled for build
        if pg_launcher.GetLaunchersCount():
            required_fields[GT(
                u'Menu Launcher')] = pg_launcher.GetRequiredFields()

            # FIXME: Old code won't work with multiple launchers
            for RF in required_fields[GT(u'Menu Launcher')]:
                Logger.Debug(
                    __name__,
                    GT(u'Required field (Menu Launcher): {}').format(
                        RF.GetName()))

        for p_name in required_fields:
            Logger.Debug(__name__, GT(u'Page name: {}').format(p_name))
            for F in required_fields[p_name]:
                if not isinstance(F, wx.StaticText) and TextIsEmpty(
                        F.GetValue()):
                    f_name = F.GetName()

                    msg_l1 = GT(u'One of the required fields is empty:')
                    msg_full = u'{}: {} ➜ {}'.format(msg_l1, p_name, f_name)

                    Logger.Warn(__name__, msg_full)

                    DetailedMessageDialog(GetMainWindow(),
                                          GT(u'Cannot Continue'),
                                          ICON_EXCLAMATION,
                                          text=msg_full).ShowModal()

                    for P in wizard.GetAllPages():
                        if P.GetTitle() == p_name:
                            Logger.Debug(
                                __name__,
                                GT(u'Showing page with required field: {}').
                                format(p_name))
                            wizard.ShowPage(P.GetId())

                    return

        if GetField(pg_files, inputid.LIST).MissingFiles():
            ShowErrorDialog(GT(u'Files are missing in file list'),
                            warn=True,
                            title=GT(u'Warning'))

            wizard.ShowPage(pgid.FILES)

            return

        ttype = GT(u'Debian Packages')
        save_dialog = GetFileSaveDialog(GetMainWindow(), GT(u'Build Package'),
                                        u'{} (*.deb)|*.deb'.format(ttype),
                                        u'deb')

        package = GetFieldValue(pg_control, inputid.PACKAGE)
        version = GetFieldValue(pg_control, inputid.VERSION)
        arch = GetFieldValue(pg_control, inputid.ARCH)
        save_dialog.SetFilename(u'{}_{}_{}.deb'.format(package, version, arch))

        if ShowDialog(save_dialog):
            self.Build(save_dialog.GetPath())

    ## Checks the final package for error with the lintian command
    #
    #  \param targetPackage
    #	Path of package to check
    def OnBuildCheckPackage(self, targetPackage):
        Logger.Debug(
            __name__,
            GT(u'Checking package "{}" for lintian errors ...').format(
                os.path.basename(targetPackage)))

        # FIXME: commands module deprecated?
        output = commands.getoutput(u'{} "{}"'.format(
            GetExecutable(u'lintian'), targetPackage))

        return output

    ## Handles the process of building the package from the formatted stage directory
    #
    #  \param stage
    #	Path to formatted temporary staged directory
    #  \param targetFile
    #	Path of the target output .deb package
    def OnBuildCreatePackage(self, stage, targetFile):
        Logger.Debug(__name__,
                     GT(u'Creating {} from {}').format(targetFile, stage))

        packager = GetExecutable(u'dpkg-deb')
        fakeroot = GetExecutable(u'fakeroot')

        if not fakeroot or not packager:
            return (dbrerrno.ENOENT, GT(u'Cannot run "fakeroot dpkg'))

        packager = os.path.basename(packager)

        Logger.Debug(__name__, GT(u'System packager: {}').format(packager))

        # DEBUG:
        cmd = u'{} {} -b "{}" "{}"'.format(fakeroot, packager, stage,
                                           targetFile)
        Logger.Debug(__name__, GT(u'Executing: {}').format(cmd))

        output = GetCommandOutput(fakeroot, (
            packager,
            u'-b',
            stage,
            targetFile,
        ))

        Logger.Debug(__name__, GT(u'Build output: {}').format(output))

        return output

    ## Retrieves total size of directory contents
    #
    #  TODO: Move this method to control page???
    #
    #  \param stage
    #	Path to formatted staged directory to scan file sizes
    #  \return
    #	<b><i>Integer</i></b> value representing installed size of all files in package
    def OnBuildGetInstallSize(self, stage):
        Logger.Debug(__name__,
                     GT(u'Retrieving installed size for {}').format(stage))

        installed_size = 0
        for ROOT, DIRS, FILES in os.walk(stage):
            for F in FILES:
                if ROOT != u'{}/DEBIAN'.format(stage).replace(u'//', u'/'):
                    F = u'{}/{}'.format(ROOT, F).replace(u'//', u'/')
                    installed_size += os.stat(F).st_size

        # Convert to kilobytes & round up
        if installed_size:
            installed_size = int(math.ceil(
                float(installed_size) / float(1024)))

        return installed_size

    ## Creates an 'md5sum' file & populates with hashes for all files contained in package
    #
    #  FIXME: Hashes for .png images (binary files???) is not the same as those
    #		 produced by debuild
    #
    #  \param stage
    #	Staged directory where files are scanned
    #  \return
    #	<b><i>String</i></b> message of result
    def OnBuildMD5Sum(self, stage):
        Logger.Debug(__name__, GT(u'Creating MD5sum file in {}').format(stage))

        WriteMD5(stage)

        return GT(u'md5sums file created: {}'.format(
            os.path.isfile(ConcatPaths((
                stage,
                u'DEBIAN/md5sums',
            )))))

    ## Retrieves list of available lintian tags (WIP)
    #
    #  TODO: Show warning dialog that this could take a while
    #  TODO: Add cancel option to progress dialog
    #  FIXME: List should be cached so no need for re-scanning
    def OnSetLintOverrides(self, event=None):
        Logger.Debug(__name__, GT(u'Setting Lintian overrides...'))

        lintian_tags_file = u'{}/data/lintian/tags'.format(PATH_app)

        if not os.path.isfile(lintian_tags_file):
            Logger.Error(
                __name__,
                u'Lintian tags file is missing: {}'.format(lintian_tags_file))

            return False

        lint_tags = RemoveEmptyLines(ReadFile(lintian_tags_file, split=True))

        if lint_tags:
            Logger.Debug(__name__, u'Lintian tags set')

            # DEBUG: Start
            if DebugEnabled() and len(lint_tags) > 50:
                print(u'  Reducing tag count to 200 ...')

                lint_tags = lint_tags[:50]

            Logger.Debug(__name__,
                         u'Processing {} tags'.format(len(lint_tags)))
            # DEBUG: End

            tag_count = len(lint_tags)

            def GetProgressMessage(message, count=tag_count):
                return u'{} ({} {})'.format(message, count, GT(u'tags'))

            progress = TimedProgressDialog(
                GetMainWindow(), GT(u'Building Tag List'),
                GetProgressMessage(GT(u'Scanning default tags')))
            progress.Start()

            wx.Yield()

            # Create the dialog
            overrides_dialog = CheckListDialog(GetMainWindow(),
                                               title=GT(u'Lintian Overrides'),
                                               allow_custom=True)
            # FIXME: Needs progress dialog
            overrides_dialog.InitCheckList(tuple(lint_tags))

            progress.SetMessage(
                GetProgressMessage(GT(u'Setting selected overrides')))

            for T in lint_tags:
                if T in self.lint_overrides:
                    overrides_dialog.SetItemCheckedByLabel(T)
                    self.lint_overrides.remove(T)

            progress.SetMessage(
                GetProgressMessage(GT(u'Adding custom tags'),
                                   len(self.lint_overrides)))

            # Remaining tags should be custom entries
            # FIXME:
            if self.lint_overrides:
                for T in self.lint_overrides:
                    overrides_dialog.AddItem(T, True)

            progress.Stop()

            if overrides_dialog.ShowModal() == wx.ID_OK:
                # Remove old overrides
                self.lint_overrides = []
                for L in overrides_dialog.GetCheckedLabels():
                    Logger.Debug(__name__,
                                 GT(u'Adding Lintian override: {}').format(L))

                    self.lint_overrides.append(L)

            return True

        else:
            Logger.Debug(__name__, u'Setting lintian tags failed')

            return False

    ## Resets page's fields to default settings
    def Reset(self):
        for O in self.build_options:
            O.SetValue(O.Default)

    ## Sets page's fields data
    #
    #  \param data
    #	<b><i>Text</i></b> to be parsed for values
    def Set(self, data):
        # ???: Redundant
        self.Reset()
        build_data = data.split(u'\n')

        if GetExecutable(u'md5sum'):
            self.chk_md5.SetValue(int(build_data[0]))

        self.chk_rmstage.SetValue(int(build_data[1]))

        if GetExecutable(u'lintian'):
            self.chk_lint.SetValue(int(build_data[2]))

    ## Sets the build summary for display & review after package is created
    def SetSummary(self, event=None):
        pg_scripts = GetPage(pgid.SCRIPTS)

        # Make sure the page is not destroyed so no error is thrown
        if self:
            # Set summary when "Build" page is shown
            # Get the file count
            files_total = GetPage(pgid.FILES).GetFileCount()
            f = GT(u'File Count')
            file_count = u'{}: {}'.format(f, files_total)

            # Scripts to make
            scripts_to_make = []
            scripts = ((u'preinst', pg_scripts.chk_preinst),
                       (u'postinst', pg_scripts.chk_postinst),
                       (u'prerm', pg_scripts.chk_prerm),
                       (u'postrm', pg_scripts.chk_postrm))

            for script in scripts:
                if script[1].IsChecked():
                    scripts_to_make.append(script[0])

            s = GT(u'Scripts')
            if len(scripts_to_make):
                scripts_to_make = u'{}: {}'.format(s,
                                                   u', '.join(scripts_to_make))

            else:
                scripts_to_make = u'{}: 0'.format(s)

            self.summary.SetValue(u'\n'.join((file_count, scripts_to_make)))
Example #4
0
    def __init__(self, parent):
        WizardPage.__init__(self, parent, pgid.BUILD)

        # Bypass build prep check
        self.prebuild_check = False

        # Add checkable items to this list
        # FIXME: Use a different method
        self.build_options = []

        # ----- Extra Options

        pnl_options = BorderedPanel(self)

        self.chk_md5 = CheckBoxESS(pnl_options,
                                   chkid.MD5,
                                   GT(u'Create md5sums file'),
                                   name=u'MD5',
                                   defaultValue=True,
                                   commands=u'md5sum')
        # The » character denotes that an alternate tooltip should be shown if the control is disabled
        self.chk_md5.tt_name = u'md5»'
        self.chk_md5.col = 0

        if UsingTest(u'alpha'):
            # Brings up control file preview for editing
            self.chk_editctrl = CheckBoxCFG(
                pnl_options,
                chkid.EDIT,
                GT(u'Preview control file for editing'),
                name=u'editctrl')
            self.chk_editctrl.col = 1

        # TODO: Use CheckBoxCFG instead of CheckBoxESS:
        #		   Fields will be set from config instead of project file

        # Option to strip binaries
        self.chk_strip = CheckBoxESS(pnl_options,
                                     chkid.STRIP,
                                     GT(u'Strip binaries'),
                                     name=u'strip»',
                                     defaultValue=True,
                                     commands=u'strip')
        self.chk_strip.col = 0

        # Deletes the temporary build tree
        self.chk_rmstage = CheckBoxESS(pnl_options,
                                       chkid.DELETE,
                                       GT(u'Delete staged directory'),
                                       name=u'RMSTAGE',
                                       defaultValue=True)
        self.chk_rmstage.col = 0

        # Checks the output .deb for errors
        self.chk_lint = CheckBoxESS(
            pnl_options,
            chkid.LINT,
            GT(u'Check package for errors with lintian'),
            name=u'LINTIAN',
            defaultValue=True,
            commands=u'lintian')
        self.chk_lint.tt_name = u'lintian»'
        self.chk_lint.col = 0

        # Installs the deb on the system
        self.chk_install = CheckBox(pnl_options,
                                    chkid.INSTALL,
                                    GT(u'Install package after build'),
                                    name=u'INSTALL',
                                    commands=(
                                        u'gdebi-gtk',
                                        u'gdebi-kde',
                                    ))
        self.chk_install.tt_name = u'install»'
        self.chk_install.col = 0

        # *** Lintian Overrides *** #

        if UsingTest(u'alpha'):
            # FIXME: Move next to lintian check box
            self.lint_overrides = []
            btn_lint_overrides = CreateButton(self,
                                              label=GT(u'Lintian overrides'))
            btn_lint_overrides.Bind(wx.EVT_BUTTON, self.OnSetLintOverrides)

        btn_build = CreateButton(self, btnid.BUILD, GT(u'Build'), u'build', 64)

        # Display log
        dsp_log = OutputLog(self)

        SetPageToolTips(self)

        # *** Event Handling *** #

        btn_build.Bind(wx.EVT_BUTTON, self.OnBuild)

        # *** Layout *** #

        lyt_options = wx.GridBagSizer()

        next_row = 0
        prev_row = next_row
        for CHK in pnl_options.Children:
            row = next_row
            FLAGS = lyt.PAD_LR

            if CHK.col:
                row = prev_row
                FLAGS = wx.RIGHT

            lyt_options.Add(CHK, (row, CHK.col), flag=FLAGS, border=5)

            if not CHK.col:
                prev_row = next_row
                next_row += 1

        pnl_options.SetSizer(lyt_options)
        pnl_options.SetAutoLayout(True)
        pnl_options.Layout()

        lyt_buttons = BoxSizer(wx.HORIZONTAL)
        lyt_buttons.Add(btn_build, 1)

        lyt_main = BoxSizer(wx.VERTICAL)
        lyt_main.AddSpacer(10)
        lyt_main.Add(wx.StaticText(self, label=GT(u'Extra Options')), 0,
                     lyt.ALGN_LB | wx.LEFT, 5)
        lyt_main.Add(pnl_options, 0, wx.LEFT, 5)
        lyt_main.AddSpacer(5)

        if UsingTest(u'alpha'):
            #lyt_main.Add(wx.StaticText(self, label=GT(u'Lintian overrides')), 0, wx.LEFT, 5)
            lyt_main.Add(btn_lint_overrides, 0, wx.LEFT, 5)

        lyt_main.AddSpacer(5)
        lyt_main.Add(lyt_buttons, 0, lyt.ALGN_C)
        lyt_main.Add(dsp_log, 2, wx.EXPAND | lyt.PAD_LRB, 5)

        self.SetAutoLayout(True)
        self.SetSizer(lyt_main)
        self.Layout()

        # *** Post-layout functions *** #

        self.InitDefaultSettings()
Example #5
0
    def __init__(self, parent, win_id=wx.ID_ANY, name=u'launcher'):
        ScrolledPanel.__init__(self, parent, win_id, name=name)

        # --- Buttons to open/preview/save .desktop file
        btn_open = CreateButton(self,
                                btnid.BROWSE,
                                GT(u'Browse'),
                                u'browse',
                                name=u'btn browse')
        btn_save = CreateButton(self,
                                btnid.SAVE,
                                GT(u'Save'),
                                u'save',
                                name=u'btn save')
        btn_preview = CreateButton(self,
                                   btnid.PREVIEW,
                                   GT(u'Preview'),
                                   u'preview',
                                   name=u'btn preview')

        # --- TYPE
        opts_type = (
            u'Application',
            u'Link',
            u'Directory',
        )

        txt_type = wx.StaticText(self, label=GT(u'Type'), name=u'type')
        ti_type = ComboBoxESS(self,
                              inputid.TYPE,
                              choices=opts_type,
                              name=u'Type',
                              defaultValue=opts_type[0])

        # --- ENCODING
        opts_enc = (
            u'UTF-1',
            u'UTF-7',
            u'UTF-8',
            u'CESU-8',
            u'UTF-EBCDIC',
            u'UTF-16',
            u'UTF-32',
            u'SCSU',
            u'BOCU-1',
            u'Punycode',
            u'GB 18030',
        )

        txt_enc = wx.StaticText(self, label=GT(u'Encoding'), name=u'encoding')
        ti_enc = ComboBoxESS(self,
                             inputid.ENC,
                             choices=opts_enc,
                             name=u'Encoding',
                             defaultValue=opts_enc[2])

        # --- TERMINAL
        chk_term = CheckBoxESS(self,
                               chkid.TERM,
                               GT(u'Terminal'),
                               name=u'Terminal')

        # --- STARTUP NOTIFY
        chk_notify = CheckBoxESS(self,
                                 chkid.NOTIFY,
                                 GT(u'Startup Notify'),
                                 name=u'StartupNotify',
                                 defaultValue=True)

        # --- NAME (menu)
        txt_name = wx.StaticText(self, label=GT(u'Name'), name=u'name*')
        ti_name = TextAreaESS(self, inputid.NAME, name=u'Name')
        ti_name.req = True

        # --- EXECUTABLE
        txt_exec = wx.StaticText(self, label=GT(u'Executable'), name=u'exec')
        ti_exec = TextAreaESS(self, inputid.EXEC, name=u'Exec')

        # --- COMMENT
        txt_comm = wx.StaticText(self, label=GT(u'Comment'), name=u'comment')
        ti_comm = TextAreaESS(self, inputid.DESCR, name=u'Comment')

        # --- ICON
        txt_icon = wx.StaticText(self, label=GT(u'Icon'), name=u'icon')
        ti_icon = TextAreaESS(self, inputid.ICON, name=u'Icon')

        txt_mime = wx.StaticText(self, label=GT(u'MIME Type'), name=u'mime')
        ti_mime = TextAreaESS(self,
                              inputid.MIME,
                              defaultValue=wx.EmptyString,
                              name=u'MimeType',
                              outLabel=u'MimeType')

        # ----- OTHER/CUSTOM
        txt_other = wx.StaticText(self,
                                  label=GT(u'Custom Fields'),
                                  name=u'other')
        btn_other = CreateButton(self,
                                 label=GT(u'Other'),
                                 image=u'add',
                                 name=u'btn other')
        btn_rm_other = CreateButton(self,
                                    btnid.REMOVE,
                                    GT(u'Remove Other'),
                                    u'remove',
                                    name=u'btn rm other')
        pnl_other = SectionedPanel(self, inputid.OTHER)

        btn_rm_other.Enable(pnl_other.HasSelected())

        # --- CATEGORIES
        opts_category = (
            u'2DGraphics',
            u'Accessibility',
            u'Application',
            u'ArcadeGame',
            u'Archiving',
            u'Audio',
            u'AudioVideo',
            u'BlocksGame',
            u'BoardGame',
            u'Calculator',
            u'Calendar',
            u'CardGame',
            u'Compression',
            u'ContactManagement',
            u'Core',
            u'DesktopSettings',
            u'Development',
            u'Dictionary',
            u'DiscBurning',
            u'Documentation',
            u'Email',
            u'FileManager',
            u'FileTransfer',
            u'Game',
            u'GNOME',
            u'Graphics',
            u'GTK',
            u'HardwareSettings',
            u'InstantMessaging',
            u'KDE',
            u'LogicGame',
            u'Math',
            u'Monitor',
            u'Network',
            u'OCR',
            u'Office',
            u'P2P',
            u'PackageManager',
            u'Photography',
            u'Player',
            u'Presentation',
            u'Printing',
            u'Qt',
            u'RasterGraphics',
            u'Recorder',
            u'RemoteAccess',
            u'Scanning',
            u'Screensaver',
            u'Security',
            u'Settings',
            u'Spreadsheet',
            u'System',
            u'Telephony',
            u'TerminalEmulator',
            u'TextEditor',
            u'Utility',
            u'VectorGraphics',
            u'Video',
            u'Viewer',
            u'WordProcessor',
            u'Wine',
            u'Wine-Programs-Accessories',
            u'X-GNOME-NetworkSettings',
            u'X-GNOME-PersonalSettings',
            u'X-GNOME-SystemSettings',
            u'X-KDE-More',
            u'X-Red-Hat-Base',
            u'X-SuSE-ControlCenter-System',
        )

        txt_category = wx.StaticText(self,
                                     label=GT(u'Categories'),
                                     name=u'category')
        btn_catclr = CreateButton(self,
                                  btnid.CLEAR,
                                  GT(u'Clear'),
                                  u'clear',
                                  name=u'clear category')
        lst_categories = CheckList(self,
                                   listid.CAT,
                                   opts_category,
                                   name=u'Categories')

        if not lst_categories.HasSelected():
            btn_catclr.Disable()

        txt_catcustom = wx.StaticText(
            self, label=GT(u'Custom Categories (Separate by "," or ";")'))
        # Set to 'True' to list custom categories first
        # FIXME: Should this be saved to project instead of config???
        chk_catcustom = CheckBoxCFG(self,
                                    chkid.CAT,
                                    GT(u'List first'),
                                    name=u'chk catcustom',
                                    cfgKey=u'prioritize custom categories')
        ti_catcustom = TextAreaESS(self, inputid.CAT2, name=u'category custom')

        # *** Event Handling *** #

        btn_open.Bind(wx.EVT_BUTTON, self.OnLoadLauncher)
        btn_save.Bind(wx.EVT_BUTTON, self.OnExportLauncher)
        btn_preview.Bind(wx.EVT_BUTTON, self.OnPreviewLauncher)

        btn_other.Bind(wx.EVT_BUTTON, self.OnOtherAdd)
        btn_rm_other.Bind(wx.EVT_BUTTON, self.OnOtherRemove)

        btn_catclr.Bind(wx.EVT_BUTTON, self.OnClearCategories)

        wx.EVT_CHECKBOX(self, inputid.OTHER, self.OnOtherSelect)
        wx.EVT_CHECKBOX(self, listid.CAT, self.OnCatSelect)

        # *** Layout *** #

        LEFT_CENTER = wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL
        LEFT_BOTTOM = wx.ALIGN_LEFT | wx.ALIGN_BOTTOM
        RIGHT_BOTTOM = wx.ALIGN_RIGHT | wx.ALIGN_BOTTOM

        lyt_opts1 = wx.FlexGridSizer()
        lyt_opts1.SetCols(3)
        lyt_opts1.SetRows(2)

        lyt_opts1.Add(txt_type, 0, LEFT_CENTER)
        lyt_opts1.Add(ti_type, 0, wx.EXPAND | wx.LEFT, 5)
        lyt_opts1.Add(chk_term, 0, LEFT_CENTER | wx.LEFT, 5)
        lyt_opts1.Add(txt_enc, 0, LEFT_CENTER | wx.TOP, 5)
        lyt_opts1.Add(ti_enc, 0, wx.LEFT | wx.TOP, 5)
        lyt_opts1.Add(chk_notify, 0, LEFT_CENTER | wx.LEFT | wx.TOP, 5)

        lyt_top = BoxSizer(wx.HORIZONTAL)
        lyt_top.Add(lyt_opts1, 0, wx.EXPAND | wx.ALIGN_BOTTOM)
        lyt_top.AddStretchSpacer(1)
        lyt_top.Add(btn_open, 0, wx.ALIGN_TOP)
        lyt_top.Add(btn_save, 0, wx.ALIGN_TOP)
        lyt_top.Add(btn_preview, 0, wx.ALIGN_TOP)

        lyt_mid = wx.GridBagSizer()
        lyt_mid.SetCols(4)
        lyt_mid.AddGrowableCol(1)
        lyt_mid.AddGrowableCol(3)

        # Row 1
        row = 0
        lyt_mid.Add(txt_name, (row, 0), flag=LEFT_CENTER)
        lyt_mid.Add(ti_name, (row, 1), flag=wx.EXPAND | wx.LEFT, border=5)
        lyt_mid.Add(txt_exec, (row, 2), flag=LEFT_CENTER | wx.LEFT, border=5)
        lyt_mid.Add(ti_exec, (row, 3), flag=wx.EXPAND | wx.LEFT, border=5)

        # Row 2
        row += 1
        lyt_mid.Add(txt_comm, (row, 0), flag=LEFT_CENTER | wx.TOP, border=5)
        lyt_mid.Add(ti_comm, (row, 1),
                    flag=wx.EXPAND | wx.LEFT | wx.TOP,
                    border=5)
        lyt_mid.Add(txt_icon, (row, 2),
                    flag=LEFT_CENTER | wx.LEFT | wx.TOP,
                    border=5)
        lyt_mid.Add(ti_icon, (row, 3),
                    flag=wx.EXPAND | wx.LEFT | wx.TOP,
                    border=5)

        # Row 3
        row += 1
        lyt_mid.Add(txt_mime, (row, 0), flag=LEFT_CENTER | wx.TOP, border=5)
        lyt_mid.Add(ti_mime, (row, 1),
                    flag=wx.EXPAND | wx.LEFT | wx.TOP,
                    border=5)

        lyt_bottom = wx.GridBagSizer()

        row = 0
        lyt_bottom.Add(txt_other, (row, 0), flag=LEFT_BOTTOM)
        lyt_bottom.Add(btn_other, (row, 1), flag=RIGHT_BOTTOM)
        lyt_bottom.Add(btn_rm_other, (row, 2), flag=RIGHT_BOTTOM)
        lyt_bottom.Add(txt_category, (row, 3),
                       flag=LEFT_BOTTOM | wx.LEFT,
                       border=5)
        lyt_bottom.Add(btn_catclr, (row, 4), flag=RIGHT_BOTTOM)

        row += 1
        lyt_bottom.Add(pnl_other, (row, 0), (3, 3), wx.EXPAND)
        lyt_bottom.Add(lst_categories, (row, 3), (1, 2), wx.EXPAND | wx.LEFT,
                       5)

        row += 1
        lyt_bottom.Add(txt_catcustom, (row, 3),
                       flag=LEFT_BOTTOM | wx.LEFT | wx.TOP,
                       border=5)
        lyt_bottom.Add(chk_catcustom, (row, 4), flag=RIGHT_BOTTOM)

        row += 1
        lyt_bottom.Add(ti_catcustom, (row, 3), (1, 2),
                       flag=wx.EXPAND | wx.LEFT,
                       border=5)

        lyt_bottom.AddGrowableRow(1)
        lyt_bottom.AddGrowableCol(1)
        lyt_bottom.AddGrowableCol(3)

        # --- Page 5 Sizer --- #
        lyt_main = BoxSizer(wx.VERTICAL)
        lyt_main.AddSpacer(5)
        lyt_main.Add(lyt_top, 0, wx.EXPAND | wx.LEFT | wx.RIGHT, 5)
        lyt_main.Add(lyt_mid, 0, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 5)
        lyt_main.Add(lyt_bottom, 1, wx.EXPAND | wx.ALL, 5)

        self.SetAutoLayout(True)
        self.SetSizer(lyt_main)
        self.Layout()