PageRenderTime 41ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/usr/lib/live-installer/frontend/gtk_interface.py

https://github.com/kdsdevs/live-installer
Python | 1924 lines | 1656 code | 145 blank | 123 comment | 110 complexity | 1309701ba8b6163aaf8b5c5446c15f36 MD5 | raw file
  1. #!/usr/bin/env python
  2. import sys
  3. sys.path.append('/usr/lib/live-installer')
  4. from installer import InstallerEngine, Setup, PartitionSetup
  5. try:
  6. import pygtk
  7. pygtk.require("2.0")
  8. import gtk
  9. import gtk.glade
  10. import gettext
  11. import os
  12. import commands
  13. import subprocess
  14. import sys
  15. import math
  16. sys.path.append('/usr/lib/live-installer')
  17. from PIL import Image
  18. import pango
  19. import threading
  20. import xml.dom.minidom
  21. from xml.dom.minidom import parse
  22. import gobject
  23. import time
  24. import webkit
  25. import GeoIP
  26. import urllib
  27. import string
  28. import parted
  29. except Exception, detail:
  30. print detail
  31. from slideshow import Slideshow
  32. gettext.install("live-installer", "/usr/share/linuxmint/locale")
  33. gtk.gdk.threads_init()
  34. INDEX_PARTITION_PATH=0
  35. INDEX_PARTITION_TYPE=1
  36. INDEX_PARTITION_DESCRIPTION=2
  37. INDEX_PARTITION_FORMAT_AS=3
  38. INDEX_PARTITION_MOUNT_AS=4
  39. INDEX_PARTITION_SIZE=5
  40. INDEX_PARTITION_FREE_SPACE=6
  41. INDEX_PARTITION_OBJECT=7
  42. class ProgressDialog:
  43. def __init__(self):
  44. self.glade = '/usr/share/live-installer/interface.glade'
  45. self.dTree = gtk.glade.XML(self.glade, 'progress_window')
  46. self.window = self.dTree.get_widget('progress_window')
  47. self.progressbar = self.dTree.get_widget('progressbar_operation')
  48. self.label = self.dTree.get_widget('label_operation')
  49. self.should_pulse = False
  50. def show(self, label=None, title=None):
  51. def pbar_pulse():
  52. if(not self.should_pulse):
  53. return False
  54. self.progressbar.pulse()
  55. return self.should_pulse
  56. if(label is not None):
  57. self.label.set_markup(label)
  58. if(title is not None):
  59. self.window.set_title(title)
  60. self.should_pulse = True
  61. self.window.show_all()
  62. gobject.timeout_add(100, pbar_pulse)
  63. def hide(self):
  64. self.should_pulse = False
  65. self.window.hide()
  66. ''' Handy. Makes message dialogs easy :D '''
  67. class MessageDialog(object):
  68. def __init__(self, title, message, style, parent=None):
  69. self.title = title
  70. self.message = message
  71. self.style = style
  72. self.parent = parent
  73. ''' Show me on screen '''
  74. def show(self):
  75. dialog = gtk.MessageDialog(self.parent, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, self.style, gtk.BUTTONS_OK, self.message)
  76. dialog.set_title(self.title)
  77. dialog.set_position(gtk.WIN_POS_CENTER)
  78. dialog.set_icon_from_file("/usr/share/icons/live-installer.png")
  79. dialog.run()
  80. dialog.destroy()
  81. class QuestionDialog(object):
  82. def __init__(self, title, message, parent=None):
  83. self.title = title
  84. self.message = message
  85. self.parent = parent
  86. ''' Show me on screen '''
  87. def show(self):
  88. dialog = gtk.MessageDialog(self.parent, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, self.message)
  89. dialog.set_title(self.title)
  90. dialog.set_position(gtk.WIN_POS_CENTER)
  91. dialog.set_icon_from_file("/usr/share/icons/live-installer.png")
  92. answer = dialog.run()
  93. if answer==gtk.RESPONSE_YES:
  94. return_value = True
  95. else:
  96. return_value = False
  97. dialog.destroy()
  98. return return_value
  99. class WizardPage:
  100. def __init__(self, help_text, icon):
  101. self.help_text = help_text
  102. self.icon = icon
  103. class Timezone:
  104. def __init__(self, name, country_code, coordinates):
  105. self.height = 409 # Height of the map
  106. self.width = 800 # Width of the map
  107. self.name = name
  108. self.country_code = country_code
  109. self.coordinates = coordinates
  110. latlongsplit = coordinates.find('-', 1)
  111. if latlongsplit == -1:
  112. latlongsplit = coordinates.find('+', 1)
  113. if latlongsplit != -1:
  114. self.latitude = coordinates[:latlongsplit]
  115. self.longitude = coordinates[latlongsplit:]
  116. else:
  117. self.latitude = coordinates
  118. self.longitude = '+0'
  119. self.latitude = self.parse_position(self.latitude, 2)
  120. self.longitude = self.parse_position(self.longitude, 3)
  121. (self.x, self.y) = self.getPosition(self.latitude, self.longitude)
  122. def parse_position(self, position, wholedigits):
  123. if position == '' or len(position) < 4 or wholedigits > 9:
  124. return 0.0
  125. wholestr = position[:wholedigits + 1]
  126. fractionstr = position[wholedigits + 1:]
  127. whole = float(wholestr)
  128. fraction = float(fractionstr)
  129. if whole >= 0.0:
  130. return whole + fraction / pow(10.0, len(fractionstr))
  131. else:
  132. return whole - fraction / pow(10.0, len(fractionstr))
  133. # @return pixel coordinate of a latitude and longitude for self
  134. # map uses Miller Projection, but is also clipped
  135. def getPosition(self, la, lo):
  136. # need to add/sub magic numbers because the map doesn't actually go from -180...180, -90...90
  137. # thus the upper corner is not -180, -90 and we have to compensate
  138. # we need a better method of determining the actually range so we can better place citites (shtylman)
  139. xdeg_offset = -6
  140. # the 180 - 35) accounts for the fact that the map does not span the entire -90 to 90
  141. # the map does span the entire 360 though, just offset
  142. x = (self.width * (180.0 + lo) / 360.0) + (self.width * xdeg_offset/ 180.0)
  143. x = x % self.width
  144. #top and bottom clipping latitudes
  145. topLat = 81
  146. bottomLat = -59
  147. #percent of entire possible range
  148. topPer = topLat/180.0
  149. # get the y in rectangular coordinates
  150. y = 1.25 * math.log(math.tan(math.pi/4.0 + 0.4 * math.radians(la)))
  151. # calculate the map range (smaller than full range because the map is clipped on top and bottom
  152. fullRange = 4.6068250867599998
  153. # the amount of the full range devoted to the upper hemisphere
  154. topOffset = fullRange*topPer
  155. mapRange = abs(1.25 * math.log(math.tan(math.pi/4.0 + 0.4 * math.radians(bottomLat))) - topOffset)
  156. # Convert to a percentage of the map range
  157. y = abs(y - topOffset)
  158. y = y / mapRange
  159. # this then becomes the percentage of the height
  160. y = y * self.height
  161. return (int(x), int(y))
  162. class InstallerWindow:
  163. def __init__(self, fullscreen=False):
  164. #Build the Setup object (where we put all our choices)
  165. self.setup = Setup()
  166. self.resource_dir = '/usr/share/live-installer/'
  167. #self.glade = 'interface.glade'
  168. self.glade = os.path.join(self.resource_dir, 'interface.glade')
  169. self.wTree = gtk.glade.XML(self.glade, 'main_window')
  170. # should be set early
  171. self.done = False
  172. self.fail = False
  173. self.paused = False
  174. # here comes the installer engine
  175. self.installer = InstallerEngine()
  176. # the distribution name
  177. DISTRIBUTION_NAME = self.installer.get_distribution_name()
  178. # load the window object
  179. self.window = self.wTree.get_widget("main_window")
  180. if "--debug" in sys.argv:
  181. self.window.set_title((_("%s Installer") % DISTRIBUTION_NAME) + " (debug)")
  182. else:
  183. self.window.set_title(_("%s Installer") % DISTRIBUTION_NAME)
  184. self.window.connect("destroy", self.quit_cb)
  185. # Wizard pages
  186. [self.PAGE_LANGUAGE, self.PAGE_PARTITIONS, self.PAGE_USER, self.PAGE_ADVANCED, self.PAGE_KEYBOARD, self.PAGE_OVERVIEW, self.PAGE_INSTALL, self.PAGE_TIMEZONE, self.PAGE_HDD, self.PAGE_CUSTOMWARNING, self.PAGE_CUSTOMPAUSED] = range(11)
  187. self.wizard_pages = range(11)
  188. self.wizard_pages[self.PAGE_LANGUAGE] = WizardPage(_("Language"), "locales.png")
  189. self.wizard_pages[self.PAGE_TIMEZONE] = WizardPage(_("Timezone"), "time.png")
  190. self.wizard_pages[self.PAGE_KEYBOARD] = WizardPage(_("Keyboard layout"), "keyboard.png")
  191. self.wizard_pages[self.PAGE_USER] = WizardPage(_("User info"), "user.png")
  192. self.wizard_pages[self.PAGE_HDD] = WizardPage(_("Hard drive"), "hdd.svg")
  193. self.wizard_pages[self.PAGE_PARTITIONS] = WizardPage(_("Partitioning"), "hdd.svg")
  194. self.wizard_pages[self.PAGE_CUSTOMWARNING] = WizardPage(_("Please make sure you wish to manually manage partitions"), "hdd.svg")
  195. self.wizard_pages[self.PAGE_ADVANCED] = WizardPage(_("Advanced options"), "advanced.png")
  196. self.wizard_pages[self.PAGE_OVERVIEW] = WizardPage(_("Summary"), "summary.png")
  197. self.wizard_pages[self.PAGE_INSTALL] = WizardPage(_("Installing Linux Mint..."), "install.png")
  198. self.wizard_pages[self.PAGE_CUSTOMPAUSED] = WizardPage(_("Installation is Paused: Please finish the custom installation"), "install.png")
  199. # set the button events (wizard_cb)
  200. self.wTree.get_widget("button_next").connect("clicked", self.wizard_cb, False)
  201. self.wTree.get_widget("button_back").connect("clicked", self.wizard_cb, True)
  202. self.wTree.get_widget("button_quit").connect("clicked", self.quit_cb)
  203. ren = gtk.CellRendererPixbuf()
  204. column = gtk.TreeViewColumn("Flags", ren)
  205. column.add_attribute(ren, "pixbuf", 2)
  206. self.wTree.get_widget("treeview_language_list").append_column(column)
  207. ren = gtk.CellRendererText()
  208. column = gtk.TreeViewColumn("Languages", ren)
  209. column.add_attribute(ren, "text", 0)
  210. self.wTree.get_widget("treeview_language_list").append_column(column)
  211. self.wTree.get_widget("treeview_language_list").connect("cursor-changed", self.assign_language)
  212. # build user info page
  213. self.wTree.get_widget("face_select_picture_button").connect( "button-release-event", self.face_select_picture_button_clicked)
  214. self.wTree.get_widget("face_take_picture_button").connect( "button-release-event", self.face_take_picture_button_clicked)
  215. os.system("convert /usr/share/pixmaps/faces/user-generic.png -resize x96 /tmp/live-installer-face.png")
  216. self.wTree.get_widget("face_image").set_from_file("/tmp/live-installer-face.png")
  217. webcam_detected = False
  218. try:
  219. import cv
  220. capture = cv.CaptureFromCAM(-1)
  221. for i in range(10):
  222. img = cv.QueryFrame(capture)
  223. if img != None:
  224. webcam_detected = True
  225. except Exception, detail:
  226. print detail
  227. if webcam_detected:
  228. self.wTree.get_widget("face_take_picture_button").set_tooltip_text(_("Click this button to take a new picture of yourself with the webcam"))
  229. else:
  230. self.wTree.get_widget("face_take_picture_button").set_sensitive(False)
  231. self.wTree.get_widget("face_take_picture_button").set_tooltip_text(_("The installer did not detect any webcam"))
  232. # build the language list
  233. self.build_lang_list()
  234. self.build_timezones()
  235. # disk view
  236. ren = gtk.CellRendererText()
  237. self.column1 = gtk.TreeViewColumn("Hard drive", ren)
  238. self.column1.add_attribute(ren, "text", 0)
  239. self.wTree.get_widget("treeview_hdds").append_column(self.column1)
  240. self.column2 = gtk.TreeViewColumn("Description", ren)
  241. self.column2.add_attribute(ren, "text", 1)
  242. self.wTree.get_widget("treeview_hdds").append_column(self.column2)
  243. self.wTree.get_widget("treeview_hdds").connect("cursor-changed", self.assign_hdd)
  244. self.build_hdds()
  245. #self.build_grub_partitions()
  246. self.wTree.get_widget("radio_hdd").set_group(self.wTree.get_widget("radio_custom"))
  247. self.wTree.get_widget("radio_hdd").connect("toggled", self.hdd_pane_toggled)
  248. self.wTree.get_widget("radio_hdd").set_active(True)
  249. self.wTree.get_widget("button_edit").connect("clicked", self.edit_partitions)
  250. self.wTree.get_widget("button_refresh").connect("clicked", self.refresh_partitions)
  251. self.wTree.get_widget("treeview_disks").connect("row_activated", self.assign_partition)
  252. self.wTree.get_widget("treeview_disks").connect( "button-release-event", self.partitions_popup_menu)
  253. # device
  254. ren = gtk.CellRendererText()
  255. self.column3 = gtk.TreeViewColumn(_("Device"), ren)
  256. self.column3.add_attribute(ren, "markup", INDEX_PARTITION_PATH)
  257. self.wTree.get_widget("treeview_disks").append_column(self.column3)
  258. # Type
  259. ren = gtk.CellRendererText()
  260. self.column4 = gtk.TreeViewColumn(_("Type"), ren)
  261. self.column4.add_attribute(ren, "markup", INDEX_PARTITION_TYPE)
  262. self.wTree.get_widget("treeview_disks").append_column(self.column4)
  263. # description
  264. ren = gtk.CellRendererText()
  265. self.column5 = gtk.TreeViewColumn(_("Operating system"), ren)
  266. self.column5.add_attribute(ren, "markup", INDEX_PARTITION_DESCRIPTION)
  267. self.wTree.get_widget("treeview_disks").append_column(self.column5)
  268. # mount point
  269. ren = gtk.CellRendererText()
  270. self.column6 = gtk.TreeViewColumn(_("Mount point"), ren)
  271. self.column6.add_attribute(ren, "markup", INDEX_PARTITION_MOUNT_AS)
  272. self.wTree.get_widget("treeview_disks").append_column(self.column6)
  273. # format
  274. ren = gtk.CellRendererText()
  275. self.column7 = gtk.TreeViewColumn(_("Format?"), ren)
  276. self.column7.add_attribute(ren, "markup", INDEX_PARTITION_FORMAT_AS)
  277. self.wTree.get_widget("treeview_disks").append_column(self.column7)
  278. # size
  279. ren = gtk.CellRendererText()
  280. self.column8 = gtk.TreeViewColumn(_("Size"), ren)
  281. self.column8.add_attribute(ren, "markup", INDEX_PARTITION_SIZE)
  282. self.wTree.get_widget("treeview_disks").append_column(self.column8)
  283. # Used space
  284. ren = gtk.CellRendererText()
  285. self.column9 = gtk.TreeViewColumn(_("Free space"), ren)
  286. self.column9.add_attribute(ren, "markup", INDEX_PARTITION_FREE_SPACE)
  287. self.wTree.get_widget("treeview_disks").append_column(self.column9)
  288. self.wTree.get_widget("entry_your_name").connect("notify::text", self.assign_realname)
  289. self.wTree.get_widget("entry_username").connect("notify::text", self.assign_username)
  290. self.wTree.get_widget("entry_hostname").connect("notify::text", self.assign_hostname)
  291. # events for detecting password mismatch..
  292. self.wTree.get_widget("entry_userpass1").connect("changed", self.assign_password)
  293. self.wTree.get_widget("entry_userpass2").connect("changed", self.assign_password)
  294. # link the checkbutton to the combobox
  295. grub_check = self.wTree.get_widget("checkbutton_grub")
  296. grub_box = self.wTree.get_widget("combobox_grub")
  297. grub_check.connect("clicked", self.assign_grub_install, grub_box)
  298. grub_box.connect("changed", self.assign_grub_device)
  299. # Install Grub by default
  300. grub_check.set_active(True)
  301. grub_box.set_sensitive(True)
  302. # kb models
  303. cell = gtk.CellRendererText()
  304. self.wTree.get_widget("combobox_kb_model").pack_start(cell, True)
  305. self.wTree.get_widget("combobox_kb_model").add_attribute(cell, 'text', 0)
  306. self.wTree.get_widget("combobox_kb_model").connect("changed", self.assign_keyboard_model)
  307. # kb layouts
  308. ren = gtk.CellRendererText()
  309. self.column10 = gtk.TreeViewColumn(_("Layout"), ren)
  310. self.column10.add_attribute(ren, "text", 0)
  311. self.wTree.get_widget("treeview_layouts").append_column(self.column10)
  312. self.wTree.get_widget("treeview_layouts").connect("cursor-changed", self.assign_keyboard_layout)
  313. ren = gtk.CellRendererText()
  314. self.column11 = gtk.TreeViewColumn(_("Variant"), ren)
  315. self.column11.add_attribute(ren, "text", 0)
  316. self.wTree.get_widget("treeview_variants").append_column(self.column11)
  317. self.wTree.get_widget("treeview_variants").connect("cursor-changed", self.assign_keyboard_variant)
  318. self.build_kb_lists()
  319. # 'about to install' aka overview
  320. ren = gtk.CellRendererText()
  321. self.column12 = gtk.TreeViewColumn(_("Overview"), ren)
  322. self.column12.add_attribute(ren, "markup", 0)
  323. self.wTree.get_widget("treeview_overview").append_column(self.column12)
  324. # install page
  325. self.wTree.get_widget("label_install_progress").set_markup("<i>%s</i>" % _("Calculating file indexes..."))
  326. #i18n
  327. self.i18n()
  328. # build partition list
  329. self.should_pulse = False
  330. # make sure we're on the right page (no pun.)
  331. self.activate_page(0)
  332. # this is a hack atm to steal the menubar's background color
  333. self.wTree.get_widget("menubar").realize()
  334. style = self.wTree.get_widget("menubar").style.copy()
  335. self.wTree.get_widget("menubar").hide()
  336. # apply to the header
  337. self.title_box = self.wTree.get_widget("title_eventbox")
  338. bgColor = gtk.gdk.color_parse('#585858')
  339. self.title_box.modify_bg(gtk.STATE_NORMAL, bgColor)
  340. fgColor = gtk.gdk.color_parse('#FFFFFF')
  341. self.help_label = self.wTree.get_widget("help_label")
  342. self.help_label.modify_fg(gtk.STATE_NORMAL, fgColor)
  343. if(fullscreen):
  344. # dedicated installer mode thingum
  345. self.window.maximize()
  346. self.window.fullscreen()
  347. #''' Launch the Slideshow '''
  348. #if ("_" in self.setup.language):
  349. # locale_code = self.setup.language.split("_")[0]
  350. #else:
  351. # locale_code = self.setup.language
  352. #slideshow_path = "/usr/share/live-installer-slideshow/slides/index.html"
  353. #if os.path.exists(slideshow_path):
  354. # browser = webkit.WebView()
  355. # s = browser.get_settings()
  356. # s.set_property('enable-file-access-from-file-uris', True)
  357. # s.set_property('enable-default-context-menu', False)
  358. # browser.open("file://" + slideshow_path + "#?locale=" + locale_code)
  359. # self.wTree.get_widget("vbox_install").add(browser)
  360. # self.wTree.get_widget("vbox_install").show_all()
  361. # Initiate the slide show
  362. self.slideshow_path = "/usr/share/live-installer/slideshow"
  363. if os.path.exists(self.slideshow_path):
  364. self.slideshow_browser = webkit.WebView()
  365. s = self.slideshow_browser.get_settings()
  366. s.set_property('enable-file-access-from-file-uris', True)
  367. s.set_property('enable-default-context-menu', False)
  368. self.slideshow_browser.open("file://" + os.path.join(self.slideshow_path, 'template.html'))
  369. self.wTree.get_widget("vbox_install").add(self.slideshow_browser)
  370. self.wTree.get_widget("vbox_install").show_all()
  371. self.browser = webkit.WebView()
  372. s = self.browser.get_settings()
  373. s.set_property('enable-file-access-from-file-uris', True)
  374. s.set_property('enable-default-context-menu', False)
  375. self.wTree.get_widget("scrolled_partitions").add(self.browser)
  376. self.window.show_all()
  377. # fix text wrap
  378. self.fix_text_wrap()
  379. def face_select_picture_button_clicked(self, widget, event):
  380. image = gtk.Image()
  381. preview = gtk.ScrolledWindow()
  382. preview.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
  383. preview.set_size_request(150, 150)
  384. preview.add_with_viewport(image)
  385. image.show()
  386. preview.show()
  387. chooser = gtk.FileChooserDialog(title=None, parent=self.window,
  388. action=gtk.FILE_CHOOSER_ACTION_OPEN,
  389. buttons=(gtk.STOCK_CANCEL,
  390. gtk.RESPONSE_CANCEL,
  391. gtk.STOCK_OPEN,
  392. gtk.RESPONSE_OK),
  393. backend=None)
  394. chooser.set_default_response(gtk.RESPONSE_OK)
  395. chooser.set_current_folder("/usr/share/pixmaps/faces")
  396. if os.path.exists("/home/mint"):
  397. chooser.add_shortcut_folder("/home/mint")
  398. filter = gtk.FileFilter()
  399. filter.set_name(_('Images'))
  400. filter.add_mime_type('image/png')
  401. filter.add_mime_type('image/jpeg')
  402. filter.add_mime_type('image/gif')
  403. filter.add_mime_type('bitmap/bmp')
  404. chooser.add_filter(filter)
  405. chooser.set_preview_widget(preview)
  406. chooser.connect("update-preview", self.update_preview_cb, preview)
  407. response = chooser.run()
  408. if response == gtk.RESPONSE_OK:
  409. filename = chooser.get_filename()
  410. os.system("convert %s -resize x96 /tmp/live-installer-face.png" % filename)
  411. self.wTree.get_widget("face_image").set_from_file("/tmp/live-installer-face.png")
  412. chooser.destroy()
  413. def update_preview_cb(self, file_chooser, preview):
  414. filename = file_chooser.get_preview_filename()
  415. try:
  416. if filename:
  417. pixbuf = gtk.gdk.pixbuf_new_from_file(filename)
  418. preview.child.child.set_from_pixbuf(pixbuf)
  419. have_preview = True
  420. else:
  421. have_preview = False
  422. except Exception, e:
  423. #print e
  424. have_preview = False
  425. file_chooser.set_preview_widget_active(have_preview)
  426. return
  427. def face_take_picture_button_clicked(self, widget, event):
  428. try:
  429. import cv
  430. capture = cv.CaptureFromCAM(-1)
  431. for i in range(10):
  432. img = cv.QueryFrame(capture)
  433. if img != None:
  434. cv.SaveImage("/tmp/live-installer-webcam.png", img)
  435. os.system("convert /tmp/live-installer-webcam.png -resize x96 /tmp/live-installer-face.png")
  436. self.wTree.get_widget("face_image").set_from_file("/tmp/live-installer-face.png")
  437. except Exception, detail:
  438. print detail
  439. def fix_text_wrap(self):
  440. while gtk.events_pending():
  441. gtk.main_iteration_do(False)
  442. # this looks bad on resize, but to handle it on resize gracefully requires quite a bit of code (to keep from lagging)
  443. width = self.window.get_size()[0] - 75
  444. # custom install warning
  445. self.wTree.get_widget("label_custom_install_directions_1").set_size_request(width, -1)
  446. self.wTree.get_widget("label_custom_install_directions_1").set_size_request(width, -1)
  447. self.wTree.get_widget("label_custom_install_directions_2").set_size_request(width, -1)
  448. self.wTree.get_widget("label_custom_install_directions_3").set_size_request(width, -1)
  449. self.wTree.get_widget("label_custom_install_directions_4").set_size_request(width, -1)
  450. self.wTree.get_widget("label_custom_install_directions_5").set_size_request(width, -1)
  451. self.wTree.get_widget("label_custom_install_directions_6").set_size_request(width, -1)
  452. # custom install installation paused directions
  453. self.wTree.get_widget("label_custom_install_paused_1").set_size_request(width, -1)
  454. self.wTree.get_widget("label_custom_install_paused_2").set_size_request(width, -1)
  455. self.wTree.get_widget("label_custom_install_paused_3").set_size_request(width, -1)
  456. self.wTree.get_widget("label_custom_install_paused_4").set_size_request(width, -1)
  457. self.wTree.get_widget("label_custom_install_paused_5").set_size_request(width, -1)
  458. def i18n(self):
  459. # about you
  460. self.wTree.get_widget("label_your_name").set_markup("<b>%s</b>" % _("Your full name"))
  461. self.wTree.get_widget("label_your_name_help").set_markup("<span fgcolor='#3C3C3C'><sub><i>%s</i></sub></span>" % _("This will be shown in the About Me application"))
  462. self.wTree.get_widget("label_username").set_markup("<b>%s</b>" % _("Your username"))
  463. self.wTree.get_widget("label_username_help").set_markup("<span fgcolor='#3C3C3C'><sub><i>%s</i></sub></span>" % _("This is the name you will use to login to your computer"))
  464. self.wTree.get_widget("label_choose_pass").set_markup("<b>%s</b>" % _("Your password"))
  465. self.wTree.get_widget("label_pass_help").set_markup("<span fgcolor='#3C3C3C'><sub><i>%s</i></sub></span>" % _("Please enter your password twice to ensure it is correct"))
  466. self.wTree.get_widget("label_hostname").set_markup("<b>%s</b>" % _("Hostname"))
  467. self.wTree.get_widget("label_hostname_help").set_markup("<span fgcolor='#3C3C3C'><sub><i>%s</i></sub></span>" % _("This hostname will be the computers name on the network"))
  468. self.wTree.get_widget("face_label").set_markup("<b>%s</b>" % _("Your picture"))
  469. self.wTree.get_widget("face_description").set_markup("<span fgcolor='#3C3C3C'><sub><i>%s</i></sub></span>" % _("This picture represents your user account. It is used in the login screen and a few other places."))
  470. self.wTree.get_widget("face_take_picture_button").set_label(_("Take a photo"))
  471. self.wTree.get_widget("face_select_picture_button").set_label(_("Select a picture"))
  472. self.wTree.get_widget("face_select_picture_button").set_tooltip_text(_("Click this button to choose a picture for your account"))
  473. # timezones
  474. self.wTree.get_widget("label_timezones").set_label(_("Selected timezone:"))
  475. # grub
  476. self.wTree.get_widget("label_grub").set_markup("<b>%s</b>" % _("Bootloader"))
  477. self.wTree.get_widget("checkbutton_grub").set_label(_("Install GRUB"))
  478. self.wTree.get_widget("label_grub_help").set_label(_("GRUB is a bootloader used to load the Linux kernel"))
  479. # keyboard page
  480. self.wTree.get_widget("label_test_kb").set_label(_("Use this box to test your keyboard layout"))
  481. self.wTree.get_widget("label_kb_model").set_label(_("Model"))
  482. # custom install warning
  483. self.wTree.get_widget("label_custom_install_directions_1").set_label(_("You have selected to manage your partitions manually, this feature is for ADVANCED USERS ONLY."))
  484. self.wTree.get_widget("label_custom_install_directions_2").set_label(_("Before continuing, please mount your target filesystem(s) at /target."))
  485. self.wTree.get_widget("label_custom_install_directions_3").set_label(_("Do NOT mount virtual devices such as /dev, /proc, /sys, etc on /target/."))
  486. self.wTree.get_widget("label_custom_install_directions_4").set_label(_("During the install, you will be given time to chroot into /target and install any pacakges that will be needed to boot your new system."))
  487. self.wTree.get_widget("label_custom_install_directions_5").set_label(_("During the install, you will be required to write your own /etc/fstab."))
  488. self.wTree.get_widget("label_custom_install_directions_6").set_label(_("If you arent sure what any of this means, please go back and deselect manual partition management."))
  489. # custom install installation paused directions
  490. self.wTree.get_widget("label_custom_install_paused_1").set_label(_("Please do the following and then click Forward to finish Installation:"))
  491. self.wTree.get_widget("label_custom_install_paused_2").set_label(_("Create /target/etc/fstab for the filesystems as they will be mounted in your new system, matching those currently mounted at /target (without using the /target prefix in the mount paths themselves)."))
  492. self.wTree.get_widget("label_custom_install_paused_3").set_label(_("Install any packages that may be needed for first boot (mdadm, cryptsetup, dmraid, etc) by calling \"sudo chroot /target\" followed by the relevant apt-get/aptitude installations."))
  493. self.wTree.get_widget("label_custom_install_paused_4").set_label(_("Note that in order for update-initramfs to work properly in some cases (such as dm-crypt), you may need to have drives currently mounted using the same block device name as they appear in /target/etc/fstab."))
  494. self.wTree.get_widget("label_custom_install_paused_5").set_label(_("Double-check that your /target/etc/fstab is correct, matches what your new system will have at first boot, and matches what is currently mounted at /target."))
  495. # hdd page
  496. self.wTree.get_widget("label_radio_hdd").set_label(_("Install Linux Mint on the selected drive:"))
  497. self.wTree.get_widget("label_radio_custom").set_label(_("Manually mount partitions (ADVANCED USERS ONLY)."))
  498. #Columns
  499. self.column1.set_title(_("Hard drive"))
  500. self.column2.set_title(_("Description"))
  501. self.column3.set_title(_("Device"))
  502. self.column4.set_title(_("Type"))
  503. self.column5.set_title(_("Operating system"))
  504. self.column6.set_title(_("Mount point"))
  505. self.column7.set_title(_("Format?"))
  506. self.column8.set_title(_("Size"))
  507. self.column9.set_title(_("Free space"))
  508. self.column10.set_title(_("Layout"))
  509. self.column11.set_title(_("Variant"))
  510. self.column12.set_title(_("Overview"))
  511. #Partitions
  512. self.wTree.get_widget("label_edit_partitions").set_label(_("Edit partitions"))
  513. def assign_realname(self, entry, prop):
  514. self.setup.real_name = entry.props.text
  515. text = entry.props.text.strip().lower()
  516. if " " in entry.props.text:
  517. elements = text.split()
  518. text = elements[0]
  519. self.setup.username = text
  520. self.wTree.get_widget("entry_username").set_text(text)
  521. self.setup.print_setup()
  522. def assign_username(self, entry, prop):
  523. self.setup.username = entry.props.text
  524. self.setup.print_setup()
  525. def assign_hostname(self, entry, prop):
  526. self.setup.hostname = entry.props.text
  527. self.setup.print_setup()
  528. def quit_cb(self, widget, data=None):
  529. ''' ask whether we should quit. because touchpads do happen '''
  530. gtk.main_quit()
  531. def assign_partition(self, widget, data=None, data2=None):
  532. ''' assign the partition ... '''
  533. model, iter = self.wTree.get_widget("treeview_disks").get_selection().get_selected()
  534. if iter is not None:
  535. row = model[iter]
  536. partition = row[INDEX_PARTITION_OBJECT]
  537. if not partition.partition.type == parted.PARTITION_EXTENDED and not partition.partition.number == -1:
  538. dlg = PartitionDialog(row[INDEX_PARTITION_PATH], row[INDEX_PARTITION_MOUNT_AS], row[INDEX_PARTITION_FORMAT_AS], row[INDEX_PARTITION_TYPE])
  539. (mount_as, format_as) = dlg.show()
  540. self.assign_mount_point(partition, mount_as, format_as)
  541. def partitions_popup_menu( self, widget, event ):
  542. if event.button == 3:
  543. model, iter = self.wTree.get_widget("treeview_disks").get_selection().get_selected()
  544. if iter is not None:
  545. partition = model.get_value(iter, INDEX_PARTITION_OBJECT)
  546. partition_type = model.get_value(iter, INDEX_PARTITION_TYPE)
  547. if not partition.partition.type == parted.PARTITION_EXTENDED and not partition.partition.number == -1 and "swap" not in partition_type:
  548. menu = gtk.Menu()
  549. menuItem = gtk.MenuItem(_("Edit"))
  550. menuItem.connect( "activate", self.assign_partition, partition)
  551. menu.append(menuItem)
  552. menuItem = gtk.SeparatorMenuItem()
  553. menu.append(menuItem)
  554. menuItem = gtk.MenuItem(_("Assign to /"))
  555. menuItem.connect( "activate", self.assign_mount_point_context_menu_wrapper, partition, "/", "ext4")
  556. menu.append(menuItem)
  557. menuItem = gtk.MenuItem(_("Assign to /home"))
  558. menuItem.connect( "activate", self.assign_mount_point_context_menu_wrapper, partition, "/home", "")
  559. menu.append(menuItem)
  560. menu.show_all()
  561. menu.popup( None, None, None, event.button, event.time )
  562. def assign_mount_point_context_menu_wrapper(self, menu, partition, mount_point, filesystem):
  563. self.assign_mount_point(partition, mount_point, filesystem)
  564. def assign_mount_point(self, partition, mount_point, filesystem):
  565. #Assign it in the treeview
  566. model = self.wTree.get_widget("treeview_disks").get_model()
  567. iter = model.get_iter_first()
  568. while iter is not None:
  569. iter_partition = model.get_value(iter, INDEX_PARTITION_OBJECT)
  570. if iter_partition == partition:
  571. model.set_value(iter, INDEX_PARTITION_MOUNT_AS, mount_point)
  572. model.set_value(iter, INDEX_PARTITION_FORMAT_AS, filesystem)
  573. else:
  574. mountpoint = model.get_value(iter, INDEX_PARTITION_MOUNT_AS)
  575. if mountpoint == mount_point:
  576. model.set_value(iter, INDEX_PARTITION_MOUNT_AS, "")
  577. model.set_value(iter, INDEX_PARTITION_FORMAT_AS, "")
  578. iter = model.iter_next(iter)
  579. #Assign it in our setup
  580. for apartition in self.setup.partitions:
  581. if (apartition.partition.path == partition.partition.path):
  582. apartition.mount_as = mount_point
  583. apartition.format_as = filesystem
  584. else:
  585. if apartition.mount_as == mount_point:
  586. apartition.mount_as = None
  587. apartition.format_as = None
  588. self.setup.print_setup()
  589. def refresh_partitions(self, widget, data=None):
  590. ''' refresh the partitions ... '''
  591. self.build_partitions()
  592. def edit_partitions(self, widget, data=None):
  593. ''' edit the partitions ... '''
  594. os.popen("gparted &")
  595. def build_lang_list(self):
  596. #Try to find out where we're located...
  597. cur_country_code = None
  598. try:
  599. whatismyip = 'http://debian.linuxmint.com/installer/show_my_ip.php'
  600. ip = urllib.urlopen(whatismyip).readlines()[0]
  601. gi = GeoIP.new(GeoIP.GEOIP_MEMORY_CACHE)
  602. cur_country_code = gi.country_code_by_addr(ip)
  603. except:
  604. pass #best effort, we get here if we're not connected to the Internet
  605. #Plan B... find out what locale we're in (i.e. USA on the live session)
  606. cur_lang = os.environ['LANG']
  607. if("." in cur_lang):
  608. cur_lang = cur_lang.split(".")[0]
  609. model = gtk.ListStore(str,str,gtk.gdk.Pixbuf)
  610. model.set_sort_column_id(0, gtk.SORT_ASCENDING)
  611. #Load countries into memory
  612. countries = {}
  613. file = open(os.path.join(self.resource_dir, 'countries'), "r")
  614. for line in file:
  615. line = line.strip()
  616. split = line.split("=")
  617. if len(split) == 2:
  618. countries[split[0]] = split[1]
  619. file.close()
  620. #Load languages into memory
  621. languages = {}
  622. file = open(os.path.join(self.resource_dir, 'languages'), "r")
  623. for line in file:
  624. line = line.strip()
  625. split = line.split("=")
  626. if len(split) == 2:
  627. languages[split[0]] = split[1]
  628. file.close()
  629. path = os.path.join(self.resource_dir, 'locales')
  630. locales = open(path, "r")
  631. cur_index = -1 # find the locale :P
  632. set_index = None
  633. for line in locales:
  634. cur_index += 1
  635. if "UTF-8" in line:
  636. locale_code = line.replace("UTF-8", "")
  637. locale_code = locale_code.replace(".", "")
  638. locale_code = locale_code.strip()
  639. if "_" in locale_code:
  640. split = locale_code.split("_")
  641. if len(split) == 2:
  642. language_code = split[0]
  643. if language_code in languages:
  644. language = languages[language_code]
  645. else:
  646. language = language_code
  647. country_code = split[1].lower()
  648. if country_code in countries:
  649. country = countries[country_code]
  650. else:
  651. country = country_code
  652. language_label = "%s (%s)" % (language, country)
  653. #language_label = "%s - %s" % (country, language)
  654. iter = model.append()
  655. model.set_value(iter, 0, language_label)
  656. model.set_value(iter, 1, locale_code)
  657. flag_path = self.resource_dir + '/flags/16/' + country_code + '.png'
  658. if os.path.exists(flag_path):
  659. model.set_value(iter, 2, gtk.gdk.pixbuf_new_from_file(flag_path))
  660. else:
  661. flag_path = self.resource_dir + '/flags/16/generic.png'
  662. model.set_value(iter, 2, gtk.gdk.pixbuf_new_from_file(flag_path))
  663. # If it's matching our country code, that's our language right there..
  664. if ((cur_country_code is not None) and (cur_country_code.lower() == country_code)):
  665. if (set_index is None):
  666. set_index = iter
  667. else:
  668. # If we find more than one language for a particular country, one of them being English, go for English by default.
  669. if (language_code == "en"):
  670. set_index = iter
  671. # Guesswork... handy for countries which have their own language (fr_FR, de_DE, es_ES.. etc. )
  672. elif (country_code == language_code):
  673. set_index = iter
  674. # as a plan B... use the locale (USA)
  675. if((set_index is None) and (locale_code == cur_lang)):
  676. set_index = iter
  677. #print "Set via locale: " + cur_lang
  678. treeview = self.wTree.get_widget("treeview_language_list")
  679. treeview.set_model(model)
  680. if(set_index is not None):
  681. column = treeview.get_column(0)
  682. path = model.get_path(set_index)
  683. treeview.set_cursor(path, focus_column=column)
  684. treeview.scroll_to_cell(path, column=column)
  685. treeview.set_search_column(0)
  686. def build_timezones(self):
  687. self.combo_timezones = self.wTree.get_widget("combo_timezones")
  688. self.combo_timezones.connect('changed', self.timezone_combo_selected)
  689. self.timezone_colors = {}
  690. self.timezone_colors["2b0000"] = "-11.0"
  691. self.timezone_colors["550000"] = "-10.0"
  692. self.timezone_colors["66ff00"] = "-9.5"
  693. self.timezone_colors["800000"] = "-9.0"
  694. self.timezone_colors["aa0000"] = "-8.0"
  695. self.timezone_colors["d40000"] = "-7.0"
  696. self.timezone_colors["ff0001"] = "-6.0"
  697. self.timezone_colors["66ff00"] = "-5.5"
  698. self.timezone_colors["ff2a2a"] = "-5.0"
  699. self.timezone_colors["c0ff00"] = "-4.5"
  700. self.timezone_colors["ff5555"] = "-4.0"
  701. self.timezone_colors["00ff00"] = "-3.5"
  702. self.timezone_colors["ff8080"] = "-3.0"
  703. self.timezone_colors["ffaaaa"] = "-2.0"
  704. self.timezone_colors["ffd5d5"] = "-1.0"
  705. self.timezone_colors["2b1100"] = "0.0"
  706. self.timezone_colors["552200"] = "1.0"
  707. self.timezone_colors["803300"] = "2.0"
  708. self.timezone_colors["aa4400"] = "3.0"
  709. self.timezone_colors["00ff66"] = "3.5"
  710. self.timezone_colors["d45500"] = "4.0"
  711. self.timezone_colors["00ccff"] = "4.5"
  712. self.timezone_colors["ff6600"] = "5.0"
  713. self.timezone_colors["0066ff"] = "5.5"
  714. self.timezone_colors["00ffcc"] = "5.75"
  715. self.timezone_colors["ff7f2a"] = "6.0"
  716. self.timezone_colors["cc00ff"] = "6.5"
  717. self.timezone_colors["ff9955"] = "7.0"
  718. self.timezone_colors["ffb380"] = "8.0"
  719. self.timezone_colors["ffccaa"] = "9.0"
  720. self.timezone_colors["a90345"] = "9.5"
  721. self.timezone_colors["ffe6d5"] = "10.0"
  722. self.timezone_colors["d10255"] = "10.5"
  723. self.timezone_colors["d4aa00"] = "11.0"
  724. self.timezone_colors["fc0266"] = "11.5"
  725. self.timezone_colors["ffcc00"] = "12.0"
  726. self.timezone_colors["fd2c80"] = "12.75"
  727. self.timezone_colors["fc5598"] = "13.0"
  728. #Add some timezones for cities which are located on borders (for which the color doesn't match the color of the rest of the timezone)
  729. self.timezone_colors["6771a9"] = "5.5" # Calcutta, India
  730. self.timezone_colors["ff7b7b"] = "-3.0" # Buenos Aires, Argentina
  731. self.timezone_colors["ff7f7f"] = "-3.0" # Rio Gallegos, Argentina
  732. self.timezone_colors["d45c27"] = "11.0" # Lord Howe, Australia
  733. self.timezone_colors["b71f54"] = "10.5" # Adelaide, Australia
  734. self.timezone_colors["d29130"] = "-4.0" # Aruba
  735. self.timezone_colors["ee5f00"] = "4.0" # Baku, Azerbaidjan
  736. self.timezone_colors["6a2a00"] = "2.0" # Sofia, Bulgaria
  737. self.timezone_colors["3c1800"] = "" # Porto Novo
  738. self.timezone_colors["3c1800"] = "1.0" # Benin
  739. self.timezone_colors["ff9898"] = "-3.0" # Maceio, Brazil
  740. self.timezone_colors["ff3f3f"] = "-4.0" # Rio Branco, Brazil
  741. self.timezone_colors["ff802c"] = "6.0" # Thimphu, Bhutan
  742. self.timezone_colors["ff0000"] = "-6.0" # Belize
  743. self.timezone_colors["11f709"] = "-3.5" # St Johns, Canada
  744. self.timezone_colors["e56347"] = "-4.0" # Curacao
  745. self.timezone_colors["cd5200"] = "4.0" # Tbilisi, Georgia
  746. self.timezone_colors["2f1300"] = "0.0" # Guernsey. UK
  747. self.timezone_colors["cea7a3"] = "0.0" # Danmarkshavn, Greenland
  748. self.timezone_colors["ff2b2b"] = "-4.0" # Thule, Greenland
  749. self.timezone_colors["79594e"] = "0.0" # Banjul, Gambia
  750. self.timezone_colors["c7a19d"] = "0.0" # Conakry, Guinea
  751. self.timezone_colors["5b3e31"] = "0.0" # Bissau, Guinea-Bissau
  752. self.timezone_colors["3f2314"] = "0.0" # Monrovia, Liberia
  753. self.timezone_colors["d515db"] = "6.5" # Rangoon, Myanmar
  754. self.timezone_colors["fd0000"] = "-7.0" # Bahia_Banderas, Mexico
  755. self.timezone_colors["ffb37f"] = "8.0" # Kuching, Malaysia
  756. self.timezone_colors["ff0066"] = "11.5" # Norfolk
  757. self.timezone_colors["351500"] = "1.0" # Lagos, Nigeria
  758. self.timezone_colors["ff8935"] = "12.75" # Chatham, New Zealand
  759. self.timezone_colors["913a00"] = "2.0" # Kigali, Rwanda
  760. self.timezone_colors["ffb17d"] = "8.0" # Singapore
  761. self.timezone_colors["ddb6b3"] = "0.0" # Freetown, Sierra Leone
  762. self.timezone_colors["ffb482"] = "9.0" # Dili, East Timor
  763. self.timezone_colors["ff5599"] = "13.0" # Tongatapu, Tonga
  764. self.timezone_colors["ff2020"] = "-5.0" # Monticello, USA
  765. self.timezone_colors["ff2525"] = "-5.0" # Marengo, USA
  766. self.timezone_colors["9d0000"] = "-9.0" # Metlakatla, Alaska/USA
  767. self.timezones = []
  768. model = gtk.ListStore(str, object)
  769. model.set_sort_column_id(0, gtk.SORT_ASCENDING)
  770. timezones = open("/usr/share/zoneinfo/zone.tab", "r")
  771. for line in timezones:
  772. if not line.strip().startswith("#"):
  773. content = line.strip().split("\t")
  774. if len(content) >= 2:
  775. country_code = content[0]
  776. coordinates = content[1]
  777. timezone = content[2]
  778. tz = Timezone(timezone, country_code, coordinates)
  779. self.timezones.append(tz)
  780. iter = model.append()
  781. model.set_value(iter, 0, timezone)
  782. model.set_value(iter, 1, tz)
  783. # Uncomment the code below to check that each timezone has a corresponding color code (the code is here for debugging only)
  784. #print "Timezone: %s, X: %s, Y: %s" % (tz.name, tz.x, tz.y)
  785. #if (tz.x <= 800 and tz.y <= 409):
  786. # im = Image.open('/usr/share/live-installer/timezone/cc.png')
  787. # rgb_im = im.convert('RGB')
  788. # hexcolor = '%02x%02x%02x' % rgb_im.getpixel((tz.x, tz.y))
  789. # print " Color: #%s" % (hexcolor)
  790. # image = "/usr/share/live-installer/timezone/timezone_%s.png" % self.timezone_colors[hexcolor]
  791. # print "Image: %s" % image
  792. cell = gtk.CellRendererText()
  793. self.combo_timezones.pack_start(cell, True)
  794. self.combo_timezones.add_attribute(cell, 'text', 0)
  795. self.combo_timezones.set_model(model)
  796. self.timezone_map = self.wTree.get_widget("image_timezones")
  797. timezone_event = self.wTree.get_widget("event_timezones")
  798. self.timezone_map.set_from_file("/usr/share/live-installer/timezone/bg.png")
  799. timezone_event.connect("button-release-event", self.timezone_map_clicked)
  800. def timezone_combo_selected(self, combobox):
  801. model = combobox.get_model()
  802. index = combobox.get_active()
  803. if index:
  804. timezone = model[index][1]
  805. self.timezone_select(timezone)
  806. def timezone_map_clicked(self, widget, event):
  807. x = event.x
  808. y = event.y
  809. print "Coords: %s %s" % (x, y)
  810. min_distance = 1000 # Looking for min, starting with a large number
  811. closest_timezone = None
  812. for timezone in self.timezones:
  813. distance = abs(x - timezone.x) + abs(y - timezone.y)
  814. if distance < min_distance:
  815. min_distance = distance
  816. closest_timezone = timezone
  817. print "Closest timezone %s" % closest_timezone.name
  818. self.timezone_select(closest_timezone)
  819. model = self.combo_timezones.get_model()
  820. iter = model.get_iter_first()
  821. while iter is not None:
  822. if closest_timezone.name == model.get_value(iter, 1).name:
  823. self.combo_timezones.set_active_iter(iter)
  824. break
  825. iter = model.iter_next(iter)
  826. def timezone_select(self, timezone):
  827. im = Image.open('/usr/share/live-installer/timezone/cc.png')
  828. rgb_im = im.convert('RGB')
  829. hexcolor = '%02x%02x%02x' % rgb_im.getpixel((timezone.x, timezone.y))
  830. print "Color: #%s" % (hexcolor)
  831. overlay_path = "/usr/share/live-installer/timezone/timezone_%s.png" % self.timezone_colors[hexcolor]
  832. print "Image: %s" % overlay_path
  833. # Superpose the picture of the timezone on the map
  834. background = Image.open("/usr/share/live-installer/timezone/bg.png")
  835. dot = Image.open("/usr/share/live-installer/timezone/dot.png")
  836. overlay = Image.open(overlay_path)
  837. background = background.convert("RGBA")
  838. overlay = overlay.convert("RGBA")
  839. dot = dot.convert("RGBA")
  840. background.paste(overlay, (0,0), overlay)
  841. background.paste(dot, (timezone.x-3, timezone.y-3), dot)
  842. background.save("/tmp/live-installer-map.png","PNG")
  843. self.timezone_map.set_from_file("/tmp/live-installer-map.png")
  844. # Save the selection
  845. self.setup.timezone = timezone.name
  846. self.setup.timezone_code = timezone.name
  847. def build_hdds(self):
  848. self.setup.disks = []
  849. model = gtk.ListStore(str, str)
  850. inxi = subprocess.Popen("inxi -c0 -D", shell=True, stdout=subprocess.PIPE)
  851. for line in inxi.stdout:
  852. line = line.rstrip("\r\n")
  853. if(line.startswith("Disks:")):
  854. line = line.replace("Disks:", "")
  855. sections = line.split(":")
  856. for section in sections:
  857. section = section.strip()
  858. if("/dev/" in section):
  859. elements = section.split()
  860. for element in elements:
  861. if "/dev/" in element:
  862. self.setup.disks.append(element)
  863. description = section.replace(element, "").strip()
  864. iter = model.append([element, description]);
  865. self.wTree.get_widget("treeview_hdds").set_model(model)
  866. if(len(self.setup.disks) > 0):
  867. # select the first HDD
  868. treeview = self.wTree.get_widget("treeview_hdds")
  869. column = treeview.get_column(0)
  870. path = model.get_path(model.get_iter_first())
  871. treeview.set_cursor(path, focus_column=column)
  872. treeview.scroll_to_cell(path, column=column)
  873. self.setup.target_disk = model.get_value(model.get_iter_first(), 0)
  874. def build_grub_partitions(self):
  875. grub_model = gtk.ListStore(str)
  876. # Add disks
  877. for disk in self.setup.disks:
  878. grub_model.append([disk])
  879. # Add partitions
  880. partitions = commands.getoutput("fdisk -l | grep ^/dev/").split("\n")
  881. for partition in partitions:
  882. try:
  883. partition = partition.split()[0].strip()
  884. if partition.startswith("/dev/"):
  885. grub_model.append([partition])
  886. except Exception, detail:
  887. print detail
  888. self.wTree.get_widget("combobox_grub").set_model(grub_model)
  889. self.wTree.get_widget("combobox_grub").set_active(0)
  890. def build_partitions(self):
  891. self.window.set_sensitive(False)
  892. # "busy" cursor.
  893. cursor = gtk.gdk.Cursor(gtk.gdk.WATCH)
  894. self.window.window.set_cursor(cursor)
  895. os.popen('mkdir -p /tmp/live-installer/tmpmount')
  896. try:
  897. #grub_model = gtk.ListStore(str)
  898. self.setup.partitions = []
  899. html_partitions = ""
  900. model = gtk.ListStore(str,str,str,str,str,str,str, object, bool, str, str, bool)
  901. model2 = gtk.ListStore(str)
  902. swap_found = False
  903. if self.setup.target_disk is not None:
  904. path = self.setup.target_disk # i.e. /dev/sda
  905. #grub_model.append([path])
  906. device = parted.getDevice(path)
  907. try:
  908. disk = parted.Disk(device)
  909. except Exception:
  910. dialog = QuestionDialog(_("Installation Tool"), _("No partition table was found on the hard drive. Do you want the installer to create a set of partitions for you? Note: This will erase any data present on the disk."), self.window)
  911. if (dialog.show()):
  912. # Create a default partition set up
  913. disk = parted.freshDisk(device, 'msdos')
  914. disk.commit()
  915. #Swap
  916. regions = disk.getFreeSpaceRegions()
  917. if len(regions) > 0:
  918. region = regions[-1]
  919. ram_size = int(commands.getoutput("cat /proc/meminfo | grep MemTotal | awk {'print $2'}")) # in KiB
  920. post_mbr_gap = parted.sizeToSectors(1, "MiB", device.sectorSize) # Grub2 requires a post-MBR gap
  921. start = post_mbr_gap
  922. num_sectors = parted.sizeToSectors(ram_size, "KiB", device.sectorSize)
  923. num_sectors = int(float(num_sectors) * 1.5) # Swap is 1.5 times bigger than RAM
  924. end = start + num_sectors
  925. cylinder = device.endSectorToCylinder(end)
  926. end = device.endCylinderToSector(cylinder)
  927. geometry = parted.Geometry(device=device, start=start, end=end)
  928. if end < region.length:
  929. partition = parted.Partition(disk=disk, type=parted.PARTITION_NORMAL, geometry=geometry)
  930. constraint = parted.Constraint(exactGeom=geometry)
  931. disk.addPartition(partition=partition, constraint=constraint)
  932. disk.commit()
  933. os.system("mkswap %s" % partition.path)
  934. #Root
  935. regions = disk.getFreeSpaceRegions()
  936. if len(regions) > 0:
  937. region = regions[-1]
  938. partition = parted.Partition(disk=disk, type=parted.PARTITION_NORMAL, geometry=region)
  939. constraint = parted.Constraint(exactGeom=region)
  940. disk.addPartition(partition=partition, constraint=constraint)
  941. disk.commit()
  942. os.system("mkfs.ext4 %s" % partition.path)
  943. self.build_partitions()
  944. return
  945. else:
  946. # Do nothing... just get out of here..
  947. raise
  948. partition = disk.getFirstPartition()
  949. last_added_partition = PartitionSetup(partition)
  950. #self.setup.partitions.append(last_added_partition)
  951. partition = partition.nextPartition()
  952. html_partitions = html_partitions + "<table width='100%'><tr>"
  953. while (partition is not None):
  954. if last_added_partition.partition.number == -1 and partition.number == -1:
  955. last_added_partition.add_partition(partition)
  956. else:
  957. last_added_partition = PartitionSetup(partition)
  958. if "swap" in last_added_partition.type:
  959. last_added_partition.type = "swap"
  960. if partition.number != -1 and "swap" not in last_added_partition.type and partition.type != parted.PARTITION_EXTENDED:
  961. #grub_model.append([partition.path])
  962. #Umount temp folder
  963. if ('/tmp/live-installer/tmpmount' in commands.getoutput('mount')):
  964. os.popen('umount /tmp/live-installer/tmpmount')
  965. #Mount partition if not mounted
  966. if (partition.path not in commands.getoutput('mount')):
  967. os.system("mount %s /tmp/live-installer/tmpmount" % partition.path)
  968. #Identify partition's description and used space
  969. if (partition.path in commands.getoutput('mount')):
  970. df_lines = commands.getoutput("df 2>/dev/null | grep %s" % partition.path).split('\n')
  971. for df_line in df_lines:
  972. df_elements = df_line.split()
  973. if df_elements[0] == partition.path:
  974. last_added_partition.used_space = df_elements[4]
  975. mount_point = df_elements[5]
  976. if "%" in last_added_partition.used_space:
  977. used_space_pct = int(last_added_partition.used_space.replace("%", "").strip())
  978. last_added_partition.free_space = int(float(last_added_partition.size) * (float(100) - float(used_space_pct)) / float(100))
  979. if os.path.exists(os.path.join(mount_point, 'etc/lsb-release')):
  980. last_added_partition.description = commands.getoutput("cat " + os.path.join(mount_point, 'etc/lsb-release') + " | grep DISTRIB_DESCRIPTION").replace('DISTRIB_DESCRIPTION', '').replace('=', '').replace('"', '').strip()
  981. if os.path.exists(os.path.join(mount_point, 'etc/issue')):
  982. last_added_partition.description = commands.getoutput("cat " + os.path.join(mount_point, 'etc/issue')).replace('\\n', '').replace('\l', '').strip()
  983. if os.path.exists(os.path.join(mount_point, 'Windows/servicing/Version')):
  984. version = commands.getoutput("ls %s" % os.path.join(mount_point, 'Windows/servicing/Version'))
  985. if version.startswith("6.1"):
  986. last_added_partition.description = "Windows 7"
  987. elif version.startswith("6.0"):
  988. last_added_partition.description = "Windows Vista"
  989. elif version.startswith("5.1") or version.startswith("5.2"):
  990. last_added_partition.description = "Windows XP"
  991. elif version.startswith("5.0"):
  992. last_added_partition.description = "Windows 2000"
  993. elif version.startswith("4.90"):
  994. last_added_partition.description = "Windows Me"
  995. elif version.startswith("4.1"):
  996. last_added_partition.description = "Windows 98"
  997. elif version.startswith("4.0.1381"):
  998. last_added_partition.description = "Windows NT"
  999. elif version.startswith("4.0.950"):
  1000. last_added_partition.description = "Windows 95"
  1001. elif os.path.exists(os.path.join(mount_point, 'Boot/BCD')):
  1002. if os.system("grep -qs \"V.i.s.t.a\" " + os.path.join(mount_point, 'Boot/BCD')) == 0:
  1003. last_added_partition.description = "Windows Vista bootloader"
  1004. elif os.system("grep -qs \"W.i.n.d.o.w.s. .7\" " + os.path.join(mount_point, 'Boot/BCD')) == 0:
  1005. last_added_partition.description = "Windows 7 bootloader"
  1006. elif os.system("grep -qs \"W.i.n.d.o.w.s. .R.e.c.o.v.e.r.y. .E.n.v.i.r.o.n.m.e.n.t\" " + os.path.join(mount_point, 'Boot/BCD')) == 0:
  1007. last_added_partition.description = "Windows recovery"
  1008. elif os.system("grep -qs \"W.i.n.d.o.w.s. .S.e.r.v.e.r. .2.0.0.8\" " + os.path.join(mount_point, 'Boot/BCD')) == 0:
  1009. last_added_partition.description = "Windows Server 2008 bootloader"
  1010. else:
  1011. last_added_partition.description = "Windows bootloader"
  1012. elif os.path.exists(os.path.join(mount_point, 'Windows/System32')):
  1013. last_added_partition.description = "Windows"
  1014. break
  1015. else:
  1016. print "Failed to mount %s" % partition.path
  1017. #Umount temp folder
  1018. if ('/tmp/live-installer/tmpmount' in commands.getoutput('mount')):
  1019. os.popen('umount /tmp/live-installer/tmpmount')
  1020. if last_added_partition.size > 1.0:
  1021. if last_added_partition.partition.type == parted.PARTITION_LOGICAL:
  1022. display_name = " " + last_added_partition.name
  1023. else:
  1024. display_name = last_added_partition.name
  1025. iter = model.append([display_name, last_added_partition.type, last_added_partition.description, "", "", '%.0f' % round(last_added_partition.size, 0), last_added_partition.free_space, last_added_partition, False, last_added_partition.start, last_added_partition.end, False]);
  1026. if last_added_partition.partition.number == -1:
  1027. model.set_value(iter, INDEX_PARTITION_TYPE, "<span foreground='#a9a9a9'>%s</span>" % last_added_partition.type)
  1028. elif last_added_partition.partition.type == parted.PARTITION_EXTENDED:
  1029. model.set_value(iter, INDEX_PARTITION_TYPE, "<span foreground='#a9a9a9'>%s</span>" % _("Extended"))
  1030. else:
  1031. if last_added_partition.type == "ntfs":
  1032. color = "#42e5ac"
  1033. elif last_added_partition.type == "fat32":
  1034. color = "#18d918"
  1035. elif last_added_partition.type == "ext4":
  1036. color = "#4b6983"
  1037. elif last_added_partition.type == "ext3":
  1038. color = "#7590ae"
  1039. elif last_added_partition.type in ["linux-swap", "swap"]:
  1040. color = "#c1665a"
  1041. last_added_partition.mount_as = "swap"
  1042. model.set_value(iter, INDEX_PARTITION_MOUNT_AS, "swap")
  1043. else:
  1044. color = "#a9a9a9"
  1045. model.set_value(iter, INDEX_PARTITION_TYPE, "<span foreground='%s'>%s</span>" % (color, last_added_partition.type))
  1046. html_partition = "<td class='partition-cell' title='$title' style='border: 3px solid $color;' width='$space%'><div class='partition'>\n <div style='width: $usage; background-color: #f8f8ba; height: 50px'></div>\n <div class='partition-text'>$path</div><div class='partition-os'>$OS</div>\n</div>\n</td>"
  1047. deviceSize = float(device.getSize()) * float(0.9) # Hack.. reducing the real size to 90% of what it is, to make sure our partitions fit..
  1048. space = int((float(partition.getSize()) / deviceSize) * float(80))
  1049. subs = {}
  1050. if (space >= 10):
  1051. subs['path'] = display_name.replace("/dev/", "")
  1052. subs['OS'] = last_added_partition.description
  1053. elif (space >= 5):
  1054. subs['path'] = display_name.replace("/dev/", "")
  1055. subs['OS'] = ""
  1056. else:
  1057. #Not enough space, don't write the name
  1058. subs['path'] = ""
  1059. subs['OS'] = ""
  1060. subs['color'] = color
  1061. if (space == 0):
  1062. space = 1
  1063. subs['space'] = space
  1064. subs['title'] = display_name + "\n" + last_added_partition.description
  1065. if "%" in last_added_partition.used_space:
  1066. subs['usage'] = last_added_partition.used_space.strip()
  1067. html_partition = string.Template(html_partition).safe_substitute(subs)
  1068. html_partitions = html_partitions + html_partition
  1069. self.setup.partitions.append(last_added_partition)
  1070. partition = partition.nextPartition()
  1071. html_partitions = html_partitions + "</tr></table>"
  1072. #self.wTree.get_widget("combobox_grub").set_model(grub_model)
  1073. #self.wTree.get_widget("combobox_grub").set_active(0)
  1074. import tempfile
  1075. html_header = "<html><head><style>body {background-color:#d6d6d6;} \
  1076. .partition{position:relative; width:100%; float: left; background: white;} \
  1077. .partition-cell{ position:relative; margin: 2px 5px 2px 0; padding: 1px; float: left; background: white;} \
  1078. .partition-text{ position:absolute; top:10; text-align: center; width=100px; left: 0; right: 0; margin: 0 auto; font-size:12px; } \
  1079. .partition-os{ position:absolute; top:30; text-align: center; width=100px; left: 0; right: 0; margin: 0 auto; font-size:10px; font-style:italic;color:#555555;} </style></head><body>"
  1080. html_footer = "</body></html>"
  1081. html = html_header + html_partitions + html_footer
  1082. # create temporary file
  1083. f = tempfile.NamedTemporaryFile(delete=False)
  1084. f.write(html)
  1085. f.close()
  1086. self.browser.open(f.name)
  1087. #browser.load_html_string(html, "file://")
  1088. self.wTree.get_widget("scrolled_partitions").show_all()
  1089. self.wTree.get_widget("treeview_disks").set_model(model)
  1090. except Exception, detail:
  1091. print detail
  1092. self.window.set_sensitive(True)
  1093. self.window.window.set_cursor(None)
  1094. def build_kb_lists(self):
  1095. ''' Do some xml kung-fu and load the keyboard stuffs '''
  1096. # firstly we'll determine the layouts in use
  1097. p = subprocess.Popen("setxkbmap -print",shell=True,stdout=subprocess.PIPE)
  1098. for line in p.stdout:
  1099. # strip it
  1100. line = line.rstrip("\r\n")
  1101. line = line.replace("{","")
  1102. line = line.replace("}","")
  1103. line = line.replace(";","")
  1104. if("xkb_symbols" in line):
  1105. # decipher the layout in use
  1106. section = line.split("\"")[1] # split by the " mark
  1107. self.setup.keyboard_layout = section.split("+")[1]
  1108. if("xkb_geometry" in line):
  1109. first_bracket = line.index("(") +1
  1110. substr = line[first_bracket:]
  1111. last_bracket = substr.index(")")
  1112. substr = substr[0:last_bracket]
  1113. keyboard_geom = substr
  1114. p.poll()
  1115. xml_file = '/usr/share/X11/xkb/rules/xorg.xml'
  1116. model_models = gtk.ListStore(str,str)
  1117. model_models.set_sort_column_id(0, gtk.SORT_ASCENDING)
  1118. model_layouts = gtk.ListStore(str,str)
  1119. model_layouts.set_sort_column_id(0, gtk.SORT_ASCENDING)
  1120. dom = parse(xml_file)
  1121. # if we find the users keyboard info we can set it in the list
  1122. set_keyboard_model = None
  1123. set_keyboard_layout = None
  1124. set_keyboard_variant = None
  1125. # grab the root element
  1126. root = dom.getElementsByTagName('xkbConfigRegistry')[0]
  1127. # build the list of models
  1128. root_models = root.getElementsByTagName('modelList')[0]
  1129. for element in root_models.getElementsByTagName('model'):
  1130. conf = element.getElementsByTagName('configItem')[0]
  1131. name = conf.getElementsByTagName('name')[0]
  1132. desc = conf.getElementsByTagName('description')[0]
  1133. #vendor = conf.getElementsByTagName('vendor')[0] # presently unused..
  1134. iter_model = model_models.append([self.getText(desc.childNodes), self.getText(name.childNodes)])
  1135. item = self.getText(name.childNodes)
  1136. if(item == keyboard_geom):
  1137. set_keyboard_model = iter_model
  1138. root_layouts = root.getElementsByTagName('layoutList')[0]
  1139. for element in root_layouts.getElementsByTagName('layout'):
  1140. conf = element.getElementsByTagName('configItem')[0]
  1141. name = conf.getElementsByTagName('name')[0]
  1142. desc = conf.getElementsByTagName('description')[0]
  1143. iter_layout = model_layouts.append([self.getText(desc.childNodes), self.getText(name.childNodes)])
  1144. item = self.getText(name.childNodes)
  1145. if(item == self.setup.keyboard_layout):
  1146. set_keyboard_layout = iter_layout
  1147. # now set the model
  1148. self.wTree.get_widget("combobox_kb_model").set_model(model_models)
  1149. self.wTree.get_widget("treeview_layouts").set_model(model_layouts)
  1150. if(set_keyboard_layout is not None):
  1151. # show it in the list
  1152. treeview = self.wTree.get_widget("treeview_layouts")
  1153. model = treeview.get_model()
  1154. column = treeview.get_column(0)
  1155. path = model.get_path(set_keyboard_layout)
  1156. treeview.set_cursor(path, focus_column=column)
  1157. treeview.scroll_to_cell(path, column=column)
  1158. if(set_keyboard_model is not None):
  1159. # show it in the combo
  1160. combo = self.wTree.get_widget("combobox_kb_model")
  1161. model = combo.get_model()
  1162. combo.set_active_iter(set_keyboard_model)
  1163. def build_kb_variant_lists(self):
  1164. # firstly we'll determine the layouts in use
  1165. p = subprocess.Popen("setxkbmap -print",shell=True,stdout=subprocess.PIPE)
  1166. for line in p.stdout:
  1167. # strip it
  1168. line = line.rstrip("\r\n")
  1169. line = line.replace("{","")
  1170. line = line.replace("}","")
  1171. line = line.replace(";","")
  1172. if("xkb_symbols" in line):
  1173. # decipher the layout in use
  1174. section = line.split("\"")[1] # split by the " mark
  1175. self.setup.keyboard_layout = section.split("+")[1]
  1176. p.poll()
  1177. xml_file = '/usr/share/X11/xkb/rules/xorg.xml'
  1178. model_variants = gtk.ListStore(str,str)
  1179. model_variants.set_sort_column_id(0, gtk.SORT_ASCENDING)
  1180. dom = parse(xml_file)
  1181. # grab the root element
  1182. root = dom.getElementsByTagName('xkbConfigRegistry')[0]
  1183. # build the list of variants
  1184. root_layouts = root.getElementsByTagName('layoutList')[0]
  1185. for layout in root_layouts.getElementsByTagName('layout'):
  1186. conf = layout.getElementsByTagName('configItem')[0]
  1187. layout_name = self.getText(conf.getElementsByTagName('name')[0].childNodes)
  1188. layout_description = self.getText(conf.getElementsByTagName('description')[0].childNodes)
  1189. if (layout_name == self.setup.keyboard_layout):
  1190. iter_variant = model_variants.append([layout_description, None])
  1191. variants_list = layout.getElementsByTagName('variantList')
  1192. if len(variants_list) > 0:
  1193. root_variants = layout.getElementsByTagName('variantList')[0]
  1194. for variant in root_variants.getElementsByTagName('variant'):
  1195. variant_conf = variant.getElementsByTagName('configItem')[0]
  1196. variant_name = self.getText(variant_conf.getElementsByTagName('name')[0].childNodes)
  1197. variant_description = "%s - %s" % (layout_description, self.getText(variant_conf.getElementsByTagName('description')[0].childNodes))
  1198. iter_variant = model_variants.append([variant_description, variant_name])
  1199. break
  1200. # now set the model
  1201. self.wTree.get_widget("treeview_variants").set_model(model_variants)
  1202. # select the first item (standard variant layout)
  1203. treeview = self.wTree.get_widget("treeview_variants")
  1204. model = treeview.get_model()
  1205. column = treeview.get_column(0)
  1206. path = model.get_path(model.get_iter_first())
  1207. treeview.set_cursor(path, focus_column=column)
  1208. def getText(self, nodelist):
  1209. rc = []
  1210. for node in nodelist:
  1211. if node.nodeType == node.TEXT_NODE:
  1212. rc.append(node.data)
  1213. return ''.join(rc)
  1214. def assign_language(self, treeview, data=None):
  1215. ''' Called whenever someone updates the language '''
  1216. model = treeview.get_model()
  1217. active = treeview.get_selection().get_selected_rows()
  1218. if(len(active) < 1):
  1219. return
  1220. active = active[1][0]
  1221. if(active is None):
  1222. return
  1223. row = model[active]
  1224. self.setup.language = row[1]
  1225. self.setup.print_setup()
  1226. try:
  1227. self.translation = gettext.translation('live-installer', "/usr/share/linuxmint/locale", languages=[self.setup.language])
  1228. self.translation.install()
  1229. except Exception, detail:
  1230. print "No translation found, switching back to English"
  1231. self.translation = gettext.translation('live-installer', "/usr/share/linuxmint/locale", languages=['en'])
  1232. self.translation.install()
  1233. try:
  1234. self.i18n()
  1235. except:
  1236. pass # Best effort. Fails the first time as self.column1 doesn't exist yet.
  1237. def assign_hdd(self, treeview, data=None):
  1238. ''' Called whenever someone updates the HDD '''
  1239. model = treeview.get_model()
  1240. active = treeview.get_selection().get_selected_rows()
  1241. if(len(active) < 1):
  1242. return
  1243. active = active[1][0]
  1244. if(active is None):
  1245. return
  1246. row = model[active]
  1247. self.setup.target_disk = row[0]
  1248. def hdd_pane_toggled(self, hdd_button):
  1249. ''' Called whenever the radio buttons on the hdd page toggle '''
  1250. if(hdd_button.get_active()):
  1251. self.wTree.get_widget("treeview_hdds").set_sensitive(True)
  1252. self.setup.skip_mount = False
  1253. else:
  1254. self.wTree.get_widget("treeview_hdds").set_sensitive(False)
  1255. self.setup.skip_mount = True
  1256. self.setup.print_setup()
  1257. def assign_grub_install(self, checkbox, grub_box, data=None):
  1258. grub_box.set_sensitive(checkbox.get_active())
  1259. if checkbox.get_active():
  1260. self.assign_grub_device(grub_box)
  1261. else:
  1262. self.setup.grub_device = None
  1263. self.setup.print_setup()
  1264. def assign_grub_device(self, combobox, data=None):
  1265. ''' Called whenever someone updates the grub device '''
  1266. model = combobox.get_model()
  1267. active = combobox.get_active()
  1268. if(active > -1):
  1269. row = model[active]
  1270. self.setup.grub_device = row[0]
  1271. self.setup.print_setup()
  1272. def assign_keyboard_model(self, combobox, data=None):
  1273. ''' Called whenever someone updates the keyboard model '''
  1274. model = combobox.get_model()
  1275. active = combobox.get_active()
  1276. if(active > -1):
  1277. row = model[active]
  1278. os.system("setxkbmap -model %s" % row[1])
  1279. self.setup.keyboard_model = row[1]
  1280. self.setup.keyboard_model_description = row[0]
  1281. self.setup.print_setup()
  1282. def assign_keyboard_layout(self, treeview, data=None):
  1283. ''' Called whenever someone updates the keyboard layout '''
  1284. model = treeview.get_model()
  1285. active = treeview.get_selection().get_selected_rows()
  1286. if(len(active) < 1):
  1287. return
  1288. active = active[1][0]
  1289. if(active is None):
  1290. return
  1291. row = model[active]
  1292. os.system("setxkbmap -layout %s" % row[1])
  1293. self.setup.keyboard_layout = row[1]
  1294. self.setup.keyboard_layout_description = row[0]
  1295. self.build_kb_variant_lists()
  1296. self.setup.print_setup()
  1297. def assign_keyboard_variant(self, treeview, data=None):
  1298. ''' Called whenever someone updates the keyboard layout '''
  1299. model = treeview.get_model()
  1300. active = treeview.get_selection().get_selected_rows()
  1301. if(len(active) < 1):
  1302. return
  1303. active = active[1][0]
  1304. if(active is None):
  1305. return
  1306. row = model[active]
  1307. if (row[1] is None):
  1308. os.system("setxkbmap -layout %s" % self.setup.keyboard_layout)
  1309. else:
  1310. os.system("setxkbmap -variant %s" % row[1])
  1311. self.setup.keyboard_variant = row[1]
  1312. self.setup.keyboard_variant_description = row[0]
  1313. self.setup.print_setup()
  1314. filename = "/tmp/live-install-keyboard-layout.png"
  1315. os.system("python /usr/lib/live-installer/frontend/generate_keyboard_layout.py %s %s %s" % (self.setup.keyboard_layout, self.setup.keyboard_variant, filename))
  1316. self.wTree.get_widget("image_keyboard").set_from_file(filename)
  1317. def assign_password(self, widget):
  1318. ''' Someone typed into the entry '''
  1319. self.setup.password1 = self.wTree.get_widget("entry_userpass1").get_text()
  1320. self.setup.password2 = self.wTree.get_widget("entry_userpass2").get_text()
  1321. if(self.setup.password1 == "" and self.setup.password2 == ""):
  1322. self.wTree.get_widget("image_mismatch").hide()
  1323. self.wTree.get_widget("label_mismatch").hide()
  1324. else:
  1325. self.wTree.get_widget("image_mismatch").show()
  1326. self.wTree.get_widget("label_mismatch").show()
  1327. if(self.setup.password1 != self.setup.password2):
  1328. self.wTree.get_widget("image_mismatch").set_from_stock(gtk.STOCK_NO, gtk.ICON_SIZE_BUTTON)
  1329. self.wTree.get_widget("label_mismatch").set_label(_("Passwords do not match"))
  1330. else:
  1331. self.wTree.get_widget("image_mismatch").set_from_stock(gtk.STOCK_OK, gtk.ICON_SIZE_BUTTON)
  1332. self.wTree.get_widget("label_mismatch").set_label(_("Passwords match"))
  1333. self.setup.print_setup()
  1334. def activate_page(self, index):
  1335. help_text = _(self.wizard_pages[index].help_text)
  1336. self.wTree.get_widget("help_label").set_markup("<big><b>%s</b></big>" % help_text)
  1337. self.wTree.get_widget("help_icon").set_from_file("/usr/share/live-installer/icons/%s" % self.wizard_pages[index].icon)
  1338. self.wTree.get_widget("notebook1").set_current_page(index)
  1339. def wizard_cb(self, widget, goback, data=None):
  1340. ''' wizard buttons '''
  1341. sel = self.wTree.get_widget("notebook1").get_current_page()
  1342. self.wTree.get_widget("button_next").set_label(_("Forward"))
  1343. self.wTree.get_widget("button_back").set_sensitive(True)
  1344. # check each page for errors
  1345. if(not goback):
  1346. if(sel == self.PAGE_LANGUAGE):
  1347. if ("_" in self.setup.language):
  1348. country_code = self.setup.language.split("_")[1]
  1349. else:
  1350. country_code = self.setup.language
  1351. combo = self.wTree.get_widget("combo_timezones")
  1352. model = combo.get_model()
  1353. iter = model.get_iter_first()
  1354. while iter is not None:
  1355. iter_country_code = model.get_value(iter, 1).country_code
  1356. if iter_country_code == country_code:
  1357. combo.set_active_iter(iter)
  1358. break
  1359. iter = model.iter_next(iter)
  1360. self.activate_page(self.PAGE_TIMEZONE)
  1361. elif (sel == self.PAGE_TIMEZONE):
  1362. if ("_" in self.setup.language):
  1363. country_code = self.setup.language.split("_")[1]
  1364. else:
  1365. country_code = self.setup.language
  1366. treeview = self.wTree.get_widget("treeview_layouts")
  1367. model = treeview.get_model()
  1368. iter = model.get_iter_first()
  1369. while iter is not None:
  1370. iter_country_code = model.get_value(iter, 1)
  1371. if iter_country_code.lower() == country_code.lower():
  1372. column = treeview.get_column(0)
  1373. path = model.get_path(iter)
  1374. treeview.set_cursor(path, focus_column=column)
  1375. treeview.scroll_to_cell(path, column=column)
  1376. break
  1377. iter = model.iter_next(iter)
  1378. self.activate_page(self.PAGE_KEYBOARD)
  1379. elif(sel == self.PAGE_KEYBOARD):
  1380. self.activate_page(self.PAGE_USER)
  1381. elif(sel == self.PAGE_USER):
  1382. errorFound = False
  1383. errorMessage = ""
  1384. if(self.setup.real_name is None or self.setup.real_name == ""):
  1385. errorFound = True
  1386. errorMessage = _("Please provide your full name")
  1387. elif(self.setup.username is None or self.setup.username == ""):
  1388. errorFound = True
  1389. errorMessage = _("Please provide a username")
  1390. elif(self.setup.password1 is None or self.setup.password1 == ""):
  1391. errorFound = True
  1392. errorMessage = _("Please provide a password for your user account")
  1393. elif(self.setup.password1 != self.setup.password2):
  1394. errorFound = True
  1395. errorMessage = _("Your passwords do not match")
  1396. elif(self.setup.hostname is None or self.setup.hostname == ""):
  1397. errorFound = True
  1398. errorMessage = _("Please provide a hostname")
  1399. else:
  1400. for char in self.setup.username:
  1401. if(char.isupper()):
  1402. errorFound = True
  1403. errorMessage = _("Your username must be lower case")
  1404. break
  1405. elif(char.isspace()):
  1406. errorFound = True
  1407. errorMessage = _("Your username may not contain whitespace")
  1408. for char in self.setup.hostname:
  1409. if(char.isupper()):
  1410. errorFound = True
  1411. errorMessage = _("Your hostname must be lower case")
  1412. break
  1413. elif(char.isspace()):
  1414. errorFound = True
  1415. errorMessage = _("Your hostname may not contain whitespace")
  1416. if (errorFound):
  1417. MessageDialog(_("Installation Tool"), errorMessage, gtk.MESSAGE_WARNING, self.window).show()
  1418. else:
  1419. self.activate_page(self.PAGE_HDD)
  1420. elif(sel == self.PAGE_HDD):
  1421. if (self.setup.skip_mount):
  1422. self.activate_page(self.PAGE_CUSTOMWARNING)
  1423. else:
  1424. self.activate_page(self.PAGE_PARTITIONS)
  1425. self.build_partitions()
  1426. elif(sel == self.PAGE_PARTITIONS):
  1427. model = self.wTree.get_widget("treeview_disks").get_model()
  1428. error = True
  1429. errorMessage = _("Please select a root (/) partition before proceeding")
  1430. for partition in self.setup.partitions:
  1431. if(partition.mount_as == "/"):
  1432. error = False
  1433. if partition.format_as is None or partition.format_as == "":
  1434. error = True
  1435. errorMessage = _("Please indicate a filesystem to format the root (/) partition before proceeding")
  1436. if(error):
  1437. MessageDialog(_("Installation Tool"), errorMessage, gtk.MESSAGE_ERROR, self.window).show()
  1438. else:
  1439. self.build_grub_partitions()
  1440. self.activate_page(self.PAGE_ADVANCED)
  1441. elif(sel == self.PAGE_CUSTOMWARNING):
  1442. self.build_grub_partitions()
  1443. self.activate_page(self.PAGE_ADVANCED)
  1444. elif(sel == self.PAGE_ADVANCED):
  1445. self.activate_page(self.PAGE_OVERVIEW)
  1446. self.show_overview()
  1447. self.wTree.get_widget("treeview_overview").expand_all()
  1448. self.wTree.get_widget("button_next").set_label(_("Install"))
  1449. elif(sel == self.PAGE_OVERVIEW):
  1450. self.activate_page(self.PAGE_INSTALL)
  1451. # do install
  1452. self.wTree.get_widget("button_next").set_sensitive(False)
  1453. self.wTree.get_widget("button_back").set_sensitive(False)
  1454. self.wTree.get_widget("button_quit").set_sensitive(False)
  1455. thr = threading.Thread(name="live-install", group=None, args=(), kwargs={}, target=self.do_install)
  1456. thr.start()
  1457. elif(sel == self.PAGE_CUSTOMPAUSED):
  1458. self.activate_page(self.PAGE_INSTALL)
  1459. self.wTree.get_widget("button_next").hide()
  1460. self.paused = False
  1461. else:
  1462. self.wTree.get_widget("button_back").set_sensitive(True)
  1463. if(sel == self.PAGE_OVERVIEW):
  1464. self.activate_page(self.PAGE_ADVANCED)
  1465. elif(sel == self.PAGE_ADVANCED):
  1466. if (self.setup.skip_mount):
  1467. self.activate_page(self.PAGE_CUSTOMWARNING)
  1468. else:
  1469. self.activate_page(self.PAGE_PARTITIONS)
  1470. elif(sel == self.PAGE_CUSTOMWARNING):
  1471. self.activate_page(self.PAGE_HDD)
  1472. elif(sel == self.PAGE_PARTITIONS):
  1473. self.activate_page(self.PAGE_HDD)
  1474. elif(sel == self.PAGE_HDD):
  1475. self.activate_page(self.PAGE_USER)
  1476. elif(sel == self.PAGE_USER):
  1477. self.activate_page(self.PAGE_KEYBOARD)
  1478. elif(sel == self.PAGE_KEYBOARD):
  1479. self.activate_page(self.PAGE_TIMEZONE)
  1480. elif(sel == self.PAGE_TIMEZONE):
  1481. self.activate_page(self.PAGE_LANGUAGE)
  1482. def show_overview(self):
  1483. ''' build the summary page '''
  1484. model = gtk.TreeStore(str)
  1485. top = model.append(None)
  1486. model.set(top, 0, _("Localization"))
  1487. iter = model.append(top)
  1488. model.set(iter, 0, _("Language: ") + "<b>%s</b>" % self.setup.language)
  1489. iter = model.append(top)
  1490. model.set(iter, 0, _("Timezone: ") + "<b>%s</b>" % self.setup.timezone)
  1491. iter = model.append(top)
  1492. if (self.setup.keyboard_variant_description is None):
  1493. model.set(iter, 0, _("Keyboard layout: ") + "<b>%s - %s</b>" % (self.setup.keyboard_model_description, self.setup.keyboard_layout_description))
  1494. else:
  1495. model.set(iter, 0, _("Keyboard layout: ") + "<b>%s - %s (%s)</b>" % (self.setup.keyboard_model_description, self.setup.keyboard_layout_description, self.setup.keyboard_variant_description))
  1496. top = model.append(None)
  1497. model.set(top, 0, _("User settings"))
  1498. iter = model.append(top)
  1499. model.set(iter, 0, _("Real name: ") + "<b>%s</b>" % self.setup.real_name)
  1500. iter = model.append(top)
  1501. model.set(iter, 0, _("Username: ") + "<b>%s</b>" % self.setup.username)
  1502. top = model.append(None)
  1503. model.set(top, 0, _("System settings"))
  1504. iter = model.append(top)
  1505. model.set(iter, 0, _("Hostname: ") + "<b>%s</b>" % self.setup.hostname)
  1506. iter = model.append(top)
  1507. if(self.setup.grub_device is not None):
  1508. model.set(iter, 0, _("Install bootloader in %s") % ("<b>%s</b>" % self.setup.grub_device))
  1509. else:
  1510. model.set(iter, 0, _("Do not install bootloader"))
  1511. top = model.append(None)
  1512. model.set(top, 0, _("Filesystem operations"))
  1513. if(self.setup.skip_mount):
  1514. iter = model.append(top)
  1515. model.set(iter, 0, "<b>Use already-mounted /target.</b>")
  1516. else:
  1517. for partition in self.setup.partitions:
  1518. if(partition.format_as is not None and partition.format_as != ""):
  1519. # format it
  1520. iter = model.append(top)
  1521. model.set(iter, 0, "<b>%s</b>" % (_("Format %(partition)s as %(format)s") % {'partition':partition.partition.path, 'format':partition.format_as}))
  1522. for partition in self.setup.partitions:
  1523. if(partition.mount_as is not None and partition.mount_as != ""):
  1524. # mount point
  1525. iter = model.append(top)
  1526. model.set(iter, 0, "<b>%s</b>" % (_("Mount %(partition)s as %(mountpoint)s") % {'partition':partition.partition.path, 'mountpoint':partition.mount_as}))
  1527. self.wTree.get_widget("treeview_overview").set_model(model)
  1528. def do_install(self):
  1529. try:
  1530. print " ## INSTALLATION "
  1531. ''' Actually perform the installation .. '''
  1532. inst = self.installer
  1533. if "--debug" in sys.argv:
  1534. print " ## DEBUG MODE - INSTALLATION PROCESS NOT LAUNCHED"
  1535. sys.exit(0)
  1536. inst.set_progress_hook(self.update_progress)
  1537. inst.set_error_hook(self.error_message)
  1538. # do we dare? ..
  1539. self.critical_error_happened = False
  1540. # Now it's time to load the slide show
  1541. if os.path.exists(self.slideshow_path):
  1542. slideThr = Slideshow(self.slideshow_browser, self.slideshow_path, self.setup.language)
  1543. # Let the slide-thread die when the parent thread dies
  1544. slideThr.daemon = True
  1545. slideThr.start()
  1546. # Start installing
  1547. do_try_finish_install = True
  1548. try:
  1549. inst.init_install(self.setup)
  1550. except Exception, detail1:
  1551. print detail1
  1552. do_try_finish_install = False
  1553. try:
  1554. gtk.gdk.threads_enter()
  1555. MessageDialog(_("Installation error"), str(detail1), gtk.MESSAGE_ERROR, self.window).show()
  1556. gtk.gdk.threads_leave()
  1557. except Exception, detail2:
  1558. print detail2
  1559. if self.critical_error_happened:
  1560. gtk.gdk.threads_enter()
  1561. MessageDialog(_("Installation error"), self.critical_error_message, gtk.MESSAGE_ERROR, self.window).show()
  1562. gtk.gdk.threads_leave()
  1563. do_try_finish_install = False
  1564. if do_try_finish_install:
  1565. if(self.setup.skip_mount):
  1566. gtk.gdk.threads_enter()
  1567. self.paused = True
  1568. self.activate_page(self.PAGE_CUSTOMPAUSED)
  1569. self.wTree.get_widget("button_next").show()
  1570. MessageDialog(_("Installation Paused"), _("Installation is now paused. Please read the instructions on the page carefully and click Forward to finish installation."), gtk.MESSAGE_INFO, self.window).show()
  1571. gtk.gdk.threads_leave()
  1572. while(self.paused):
  1573. time.sleep(0.1)
  1574. try:
  1575. inst.finish_install(self.setup)
  1576. except Exception, detail1:
  1577. print detail1
  1578. try:
  1579. gtk.gdk.threads_enter()
  1580. MessageDialog(_("Installation error"), str(detail1), gtk.MESSAGE_ERROR, self.window).show()
  1581. gtk.gdk.threads_leave()
  1582. except Exception, detail2:
  1583. print detail2
  1584. # show a message dialog thingum
  1585. while(not self.done):
  1586. time.sleep(0.1)
  1587. if self.critical_error_happened:
  1588. gtk.gdk.threads_enter()
  1589. MessageDialog(_("Installation error"), self.critical_error_message, gtk.MESSAGE_ERROR, self.window).show()
  1590. gtk.gdk.threads_leave()
  1591. else:
  1592. gtk.gdk.threads_enter()
  1593. dialog = QuestionDialog(_("Installation finished"), _("Installation is now complete. Do you want to restart your computer to use the new system?"), self.window)
  1594. if (dialog.show()):
  1595. # Reboot now
  1596. os.system('reboot')
  1597. gtk.gdk.threads_leave()
  1598. print " ## INSTALLATION COMPLETE "
  1599. except Exception, detail:
  1600. print "!!!! General exception"
  1601. print detail
  1602. # safe??
  1603. gtk.main_quit()
  1604. # you are now..
  1605. sys.exit(0)
  1606. def error_message(self, message=""):
  1607. self.critical_error_happened = True
  1608. self.critical_error_message = message
  1609. def update_progress(self, fail=False, done=False, pulse=False, total=0,current=0,message=""):
  1610. #print "%d/%d: %s" % (current, total, message)
  1611. # TODO: ADD FAIL CHECKS..
  1612. if(pulse):
  1613. gtk.gdk.threads_enter()
  1614. self.wTree.get_widget("label_install_progress").set_label(message)
  1615. gtk.gdk.threads_leave()
  1616. self.do_progress_pulse(message)
  1617. return
  1618. if(done):
  1619. # cool, finished :D
  1620. self.should_pulse = False
  1621. self.done = done
  1622. gtk.gdk.threads_enter()
  1623. self.wTree.get_widget("progressbar").set_fraction(1)
  1624. self.wTree.get_widget("label_install_progress").set_label(message)
  1625. gtk.gdk.threads_leave()
  1626. return
  1627. self.should_pulse = False
  1628. _total = float(total)
  1629. _current = float(current)
  1630. pct = float(_current/_total)
  1631. szPct = int(pct)
  1632. # thread block
  1633. gtk.gdk.threads_enter()
  1634. self.wTree.get_widget("progressbar").set_fraction(pct)
  1635. self.wTree.get_widget("label_install_progress").set_label(message)
  1636. gtk.gdk.threads_leave()
  1637. # end thread block
  1638. def do_progress_pulse(self, message):
  1639. def pbar_pulse():
  1640. if(not self.should_pulse):
  1641. return False
  1642. gtk.gdk.threads_enter()
  1643. self.wTree.get_widget("progressbar").pulse()
  1644. gtk.gdk.threads_leave()
  1645. return self.should_pulse
  1646. if(not self.should_pulse):
  1647. self.should_pulse = True
  1648. gobject.timeout_add(100, pbar_pulse)
  1649. else:
  1650. # asssume we're "pulsing" already
  1651. self.should_pulse = True
  1652. pbar_pulse()
  1653. class PartitionDialog:
  1654. def __init__(self, path, mount_as, format_as, type):
  1655. self.resource_dir = '/usr/share/live-installer/'
  1656. self.glade = os.path.join(self.resource_dir, 'interface.glade')
  1657. self.dTree = gtk.glade.XML(self.glade, 'dialog')
  1658. self.window = self.dTree.get_widget("dialog")
  1659. self.window.set_title(_("Edit partition"))
  1660. ''' Build supported filesystems list '''
  1661. model = gtk.ListStore(str)
  1662. model.append([""])
  1663. if "swap" in type:
  1664. model.append(["swap"])
  1665. else:
  1666. try:
  1667. for item in os.listdir("/sbin"):
  1668. if(item.startswith("mkfs.")):
  1669. fstype = item.split(".")[1]
  1670. model.append([fstype])
  1671. except Exception, detail:
  1672. print detail
  1673. print _("Could not build supported filesystems list!")
  1674. self.dTree.get_widget("combobox_use_as").set_model(model)
  1675. if "swap" in type:
  1676. mounts = ["", "swap"]
  1677. else:
  1678. mounts = ["", "/", "/boot", "/tmp", "/home", "/srv"]
  1679. model = gtk.ListStore(str)
  1680. for mount in mounts:
  1681. model.append([mount])
  1682. self.dTree.get_widget("comboboxentry_mount_point").set_model(model)
  1683. # i18n
  1684. self.dTree.get_widget("label_partition").set_markup("<b>%s</b>" % _("Device:"))
  1685. self.dTree.get_widget("label_partition_value").set_label(path)
  1686. self.dTree.get_widget("label_use_as").set_markup(_("Format as:"))
  1687. cur = -1
  1688. model = self.dTree.get_widget("combobox_use_as").get_model()
  1689. for item in model:
  1690. cur += 1
  1691. if(item[0] == format_as):
  1692. self.dTree.get_widget("combobox_use_as").set_active(cur)
  1693. break
  1694. self.dTree.get_widget("label_mount_point").set_markup(_("Mount point:"))
  1695. self.dTree.get_widget("comboboxentry_mount_point").child.set_text(mount_as)
  1696. def show(self):
  1697. self.window.run()
  1698. self.window.hide()
  1699. w = self.dTree.get_widget("comboboxentry_mount_point")
  1700. w.child.get_text().replace(" ","")
  1701. mount_as = w.child.get_text()
  1702. w = self.dTree.get_widget("combobox_use_as")
  1703. # find filesystem ..
  1704. active = w.get_active()
  1705. model = w.get_model()[active]
  1706. format_as = model[0]
  1707. return (mount_as, format_as)