idb 2.9.0 → 2.9.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +30 -0
- data/Gemfile.lock +5 -0
- data/idb.gemspec +1 -0
- data/lib/gui/app_binary_tab_widget.rb +7 -14
- data/lib/gui/app_details_group_box.rb +63 -88
- data/lib/gui/app_list_dialog.rb +29 -35
- data/lib/gui/app_list_widget_item.rb +1 -5
- data/lib/gui/app_tab_widget.rb +17 -22
- data/lib/gui/binary_strings_widget.rb +7 -15
- data/lib/gui/ca_manager_dialog.rb +32 -54
- data/lib/gui/cache_db_widget.rb +21 -26
- data/lib/gui/certificate_item.rb +2 -2
- data/lib/gui/default_protection_class_group_widget.rb +7 -12
- data/lib/gui/device_info_group_box.rb +26 -23
- data/lib/gui/main_tab_widget.rb +2 -21
- data/lib/gui/shared_libraries_widget.rb +1 -1
- data/lib/gui/sqlite_widget.rb +1 -3
- data/lib/gui/weak_class_dump_widget.rb +1 -1
- data/lib/idb.rb +3 -3
- data/lib/idb/version.rb +1 -1
- data/lib/lib/abstract_device.rb +7 -11
- data/lib/lib/app.rb +49 -59
- data/lib/lib/app_binary.rb +18 -29
- data/lib/lib/ca_interface.rb +46 -59
- data/lib/lib/device.rb +68 -155
- data/lib/lib/device_ca_interface.rb +7 -13
- data/lib/lib/host_file_wrapper.rb +6 -8
- data/lib/lib/ios8_last_launch_services_map_wrapper.rb +11 -18
- data/lib/lib/local_operations.rb +24 -32
- data/lib/lib/otool_wrapper.rb +30 -33
- data/lib/lib/rsync_git_manager.rb +26 -22
- data/lib/lib/screen_shot_util.rb +20 -28
- data/lib/lib/settings.rb +14 -17
- data/lib/lib/simulator.rb +11 -16
- data/lib/lib/simulator_ca_interface.rb +1 -3
- data/lib/lib/ssh_operations.rb +49 -65
- data/lib/lib/ssh_port_forwarder.rb +9 -13
- data/lib/lib/tools.rb +3 -3
- data/lib/lib/url_scheme_fuzzer.rb +41 -49
- data/lib/lib/usb_muxd_wrapper.rb +6 -8
- data/lib/lib/weak_class_dump_wrapper.rb +15 -16
- metadata +19 -9
- data/lib/gui/console_widget.rb +0 -163
- data/lib/gui/cycript_console_widget.rb +0 -68
- data/lib/gui/cycript_thread.rb +0 -81
- data/lib/lib/console_launcher.rb +0 -24
- data/lib/lib/i_device_diagnostics_wrapper.rb +0 -90
- data/lib/lib/snoop_it_wrapper.rb +0 -80
data/lib/gui/app_tab_widget.rb
CHANGED
@@ -4,46 +4,43 @@ module Idb
|
|
4
4
|
signals "app_changed()"
|
5
5
|
signals "binary_analyzed()"
|
6
6
|
|
7
|
-
def initialize
|
8
|
-
super
|
7
|
+
def initialize(*args)
|
8
|
+
super(*args)
|
9
9
|
|
10
10
|
@layout = Qt::GridLayout.new self
|
11
11
|
setLayout(@layout)
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
@layout.addWidget @select_app_button, 0,0
|
13
|
+
@layout.addWidget @select_app_button, 0, 0
|
16
14
|
|
17
15
|
# Box for App details
|
18
16
|
@app_details = AppDetailsGroupBox.new @central_widget
|
19
|
-
@app_details.connect(SIGNAL(:show_device_status))
|
17
|
+
@app_details.connect(SIGNAL(:show_device_status)) do
|
20
18
|
@device_status = DeviceStatusDialog.new
|
21
19
|
@device_status.exec
|
22
|
-
|
20
|
+
end
|
23
21
|
|
24
|
-
|
25
|
-
@layout.addWidget @app_details, 0,0,2,1
|
22
|
+
@layout.addWidget @app_details, 0, 0, 2, 1
|
26
23
|
|
27
24
|
# Entitlements
|
28
25
|
@app_entitlements = AppEntitlementsGroupBox.new @central_widget
|
29
|
-
@layout.addWidget @app_entitlements, 2,0, 1, 2
|
26
|
+
@layout.addWidget @app_entitlements, 2, 0, 1, 2
|
30
27
|
|
31
28
|
# App Binary Details
|
32
29
|
@app_binary = AppBinaryGroupBox.new @central_widget
|
33
|
-
@layout.addWidget @app_binary, 0,1
|
34
|
-
@app_binary.connect(SIGNAL('binary_analyzed()'))
|
35
|
-
emit binary_analyzed
|
36
|
-
|
37
|
-
|
38
|
-
@spacer_horizontal = Qt::SpacerItem.new 0,1,
|
30
|
+
@layout.addWidget @app_binary, 0, 1
|
31
|
+
@app_binary.connect(SIGNAL('binary_analyzed()')) do
|
32
|
+
emit binary_analyzed
|
33
|
+
end
|
34
|
+
|
35
|
+
@spacer_horizontal = Qt::SpacerItem.new 0, 1,
|
36
|
+
Qt::SizePolicy::Expanding,
|
37
|
+
Qt::SizePolicy::Fixed
|
39
38
|
@layout.addItem @spacer_horizontal, 0, 3
|
40
39
|
|
41
|
-
@spacer = Qt::SpacerItem.new 0,1, Qt::SizePolicy::Fixed, Qt::SizePolicy::Expanding
|
42
|
-
@spacer2 = Qt::SpacerItem.new 0,1, Qt::SizePolicy::Fixed, Qt::SizePolicy::Expanding
|
40
|
+
@spacer = Qt::SpacerItem.new 0, 1, Qt::SizePolicy::Fixed, Qt::SizePolicy::Expanding
|
41
|
+
@spacer2 = Qt::SpacerItem.new 0, 1, Qt::SizePolicy::Fixed, Qt::SizePolicy::Expanding
|
43
42
|
|
44
43
|
@layout.addItem @spacer, 3, 0
|
45
|
-
|
46
|
-
|
47
44
|
end
|
48
45
|
|
49
46
|
def app_changed
|
@@ -51,8 +48,6 @@ module Idb
|
|
51
48
|
@app_details.app_changed
|
52
49
|
@app_entitlements.clear
|
53
50
|
@app_entitlements.app_changed
|
54
|
-
|
55
51
|
end
|
56
|
-
|
57
52
|
end
|
58
53
|
end
|
@@ -1,33 +1,25 @@
|
|
1
1
|
module Idb
|
2
2
|
class BinaryStringsWidget < Qt::Widget
|
3
|
-
|
4
|
-
|
5
|
-
super *args
|
3
|
+
def initialize(*args)
|
4
|
+
super(*args)
|
6
5
|
@layout = Qt::GridLayout.new
|
7
6
|
setLayout(@layout)
|
8
7
|
|
9
8
|
@details = Qt::PlainTextEdit.new
|
10
9
|
@details.setReadOnly(true)
|
11
10
|
|
12
|
-
|
13
11
|
@extract = Qt::PushButton.new "Extract Strings"
|
14
|
-
@extract.connect(SIGNAL
|
12
|
+
@extract.connect(SIGNAL(:released)) do
|
15
13
|
@details.clear
|
16
14
|
strings = $selected_app.strings
|
17
15
|
@details.appendPlainText(strings)
|
18
|
-
|
19
|
-
|
20
|
-
@layout.addWidget @details, 0,0
|
21
|
-
@layout.addWidget @extract, 1,0
|
22
|
-
|
23
|
-
|
24
|
-
|
16
|
+
end
|
25
17
|
|
18
|
+
@layout.addWidget @details, 0, 0
|
19
|
+
@layout.addWidget @extract, 1, 0
|
26
20
|
end
|
27
21
|
|
28
22
|
def refresh
|
29
|
-
|
30
23
|
end
|
31
|
-
|
32
24
|
end
|
33
|
-
end
|
25
|
+
end
|
@@ -1,17 +1,13 @@
|
|
1
1
|
require_relative 'certificate_item'
|
2
2
|
|
3
3
|
module Idb
|
4
|
-
|
5
4
|
class CAManagerDialog < Qt::Dialog
|
6
|
-
|
7
|
-
|
8
|
-
super *args
|
5
|
+
def initialize(*args)
|
6
|
+
super(*args)
|
9
7
|
@layout = Qt::GridLayout.new
|
10
8
|
setLayout(@layout)
|
11
9
|
setWindowTitle("CA Certificate Management")
|
12
10
|
|
13
|
-
|
14
|
-
|
15
11
|
@model = Qt::StandardItemModel.new
|
16
12
|
|
17
13
|
@selection_model = Qt::ItemSelectionModel.new @model
|
@@ -22,50 +18,46 @@ module Idb
|
|
22
18
|
@cert_tab.setSelectionModel(@selection_model)
|
23
19
|
|
24
20
|
@cert_tab.setSelectionBehavior(Qt::AbstractItemView::SelectRows)
|
25
|
-
@cert_tab.setEditTriggers(Qt::AbstractItemView::NoEditTriggers
|
26
|
-
|
21
|
+
@cert_tab.setEditTriggers(Qt::AbstractItemView::NoEditTriggers)
|
27
22
|
|
28
|
-
@selection_model.connect(SIGNAL('selectionChanged(QItemSelection,QItemSelection)'))
|
29
|
-
if x.indexes.length
|
23
|
+
@selection_model.connect(SIGNAL('selectionChanged(QItemSelection,QItemSelection)')) do |x, _|
|
24
|
+
if x.indexes.length.zero?
|
30
25
|
@delete_button.setEnabled(false)
|
31
26
|
else
|
32
27
|
@delete_button.setEnabled(true)
|
33
28
|
@selected_row = x.indexes[0].row
|
34
29
|
end
|
35
|
-
|
36
|
-
}
|
37
|
-
|
30
|
+
end
|
38
31
|
|
39
32
|
@refresh_button = Qt::PushButton.new "Refresh"
|
40
|
-
@refresh_button.connect(SIGNAL(:released))
|
33
|
+
@refresh_button.connect(SIGNAL(:released)) do |_x|
|
41
34
|
refresh_table
|
42
|
-
|
35
|
+
end
|
43
36
|
|
44
37
|
@delete_button = Qt::PushButton.new "Delete"
|
45
38
|
@delete_button.setEnabled(false)
|
46
|
-
@delete_button.connect(SIGNAL(:released))
|
39
|
+
@delete_button.connect(SIGNAL(:released)) do |_x|
|
47
40
|
item_containing_cert = @model.takeRow(@selected_row)[0]
|
48
|
-
|
41
|
+
unless item_containing_cert.nil?
|
49
42
|
@if.remove_cert item_containing_cert.certificate
|
50
43
|
end
|
51
44
|
refresh_table
|
52
|
-
|
53
|
-
|
45
|
+
end
|
54
46
|
|
55
47
|
@import_button = Qt::PushButton.new "Import..."
|
56
48
|
@import_button.setToolTip("Import an existing certificate")
|
57
|
-
@import_button.connect(SIGNAL(:released))
|
49
|
+
@import_button.connect(SIGNAL(:released)) do |_x|
|
58
50
|
@file_dialog = Qt::FileDialog.new
|
59
51
|
@file_dialog.setAcceptMode(Qt::FileDialog::AcceptOpen)
|
60
|
-
filters =
|
52
|
+
filters = []
|
61
53
|
filters << "PEM Files (*.pem)"
|
62
54
|
filters << "Any files (*)"
|
63
|
-
@file_dialog.setNameFilters(filters)
|
55
|
+
@file_dialog.setNameFilters(filters)
|
64
56
|
|
65
|
-
@file_dialog.connect(SIGNAL('fileSelected(QString)'))
|
57
|
+
@file_dialog.connect(SIGNAL('fileSelected(QString)')) do |x|
|
66
58
|
begin
|
67
59
|
@if.server_cert(x)
|
68
|
-
rescue
|
60
|
+
rescue StandardError => e
|
69
61
|
error = Qt::MessageBox.new self
|
70
62
|
error.setInformativeText("Couldn't import certificate")
|
71
63
|
error.setDetailedText(e.message)
|
@@ -73,33 +65,29 @@ module Idb
|
|
73
65
|
error.exec
|
74
66
|
end
|
75
67
|
refresh_table
|
76
|
-
|
77
|
-
|
78
|
-
|
68
|
+
end
|
79
69
|
|
80
70
|
@file_dialog.exec
|
81
|
-
|
71
|
+
end
|
82
72
|
|
83
73
|
@close_button = Qt::PushButton.new "Close"
|
84
|
-
@close_button.connect(SIGNAL(:released))
|
74
|
+
@close_button.connect(SIGNAL(:released)) do |_x|
|
85
75
|
@if.stop_cert_server
|
86
|
-
reject
|
87
|
-
|
76
|
+
reject
|
77
|
+
end
|
88
78
|
|
89
79
|
@layout.addWidget @cert_tab, 0, 0, 4, 4
|
90
80
|
@layout.addWidget @refresh_button, 0, 4
|
91
|
-
@layout.addWidget @delete_button, 1,4
|
92
|
-
@layout.addWidget @import_button, 2,4
|
93
|
-
|
81
|
+
@layout.addWidget @delete_button, 1, 4
|
82
|
+
@layout.addWidget @import_button, 2, 4
|
83
|
+
spacer_item = Qt::SpacerItem.new(0, 1, Qt::SizePolicy::Fixed, Qt::SizePolicy::Expanding)
|
84
|
+
@layout.addItem spacer_item, 3, 4
|
94
85
|
@layout.addWidget @close_button, 4, 4
|
95
86
|
|
96
87
|
@if = $device.ca_interface
|
97
88
|
refresh_table
|
98
89
|
|
99
|
-
|
100
|
-
|
101
|
-
#setFixedHeight(sizeHint().height());
|
102
|
-
setMinimumSize(800,500)
|
90
|
+
setMinimumSize(800, 500)
|
103
91
|
end
|
104
92
|
|
105
93
|
def refresh_table
|
@@ -108,30 +96,20 @@ module Idb
|
|
108
96
|
@model.setHorizontalHeaderItem(1, Qt::StandardItem.new("Expiry"))
|
109
97
|
@model.setHorizontalHeaderItem(2, Qt::StandardItem.new("Issuer"))
|
110
98
|
|
111
|
-
@if.
|
112
|
-
row =
|
113
|
-
item = CertificateItem.new(cert.subject.to_a.map { |x| "#{x[0]}: #{x[1]}"}.join("\n"))
|
99
|
+
@if.certs.each do |cert|
|
100
|
+
row = []
|
101
|
+
item = CertificateItem.new(cert.subject.to_a.map { |x| "#{x[0]}: #{x[1]}" }.join("\n"))
|
114
102
|
item.certificate = cert
|
115
103
|
row << item
|
116
104
|
row << Qt::StandardItem.new(cert.not_after.to_s)
|
117
|
-
row << Qt::StandardItem.new(cert.issuer.to_a.map { |x| "#{x[0]}: #{x[1]}"}.join("\n"))
|
105
|
+
row << Qt::StandardItem.new(cert.issuer.to_a.map { |x| "#{x[0]}: #{x[1]}" }.join("\n"))
|
118
106
|
@model.appendRow(row)
|
119
|
-
|
107
|
+
end
|
120
108
|
@cert_tab.resizeColumnsToContents
|
121
109
|
@cert_tab.resizeRowsToContents
|
122
110
|
|
123
|
-
|
124
111
|
# puts "#{i.to_s.ljust(2)} - Subject: #{cert.subject}"
|
125
112
|
# puts " Details: #{cert.inspect}"
|
126
|
-
|
127
|
-
|
128
|
-
|
129
113
|
end
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
114
|
end
|
136
|
-
|
137
|
-
end
|
115
|
+
end
|
data/lib/gui/cache_db_widget.rb
CHANGED
@@ -1,33 +1,27 @@
|
|
1
1
|
module Idb
|
2
2
|
class CacheDbWidget < Qt::Widget
|
3
|
-
|
4
|
-
|
5
|
-
super *args
|
3
|
+
def initialize(*args)
|
4
|
+
super(*args)
|
6
5
|
|
7
6
|
@refresh = Qt::PushButton.new "Refresh"
|
8
|
-
@refresh.connect(SIGNAL
|
7
|
+
@refresh.connect(SIGNAL(:released)) do
|
9
8
|
refresh
|
10
|
-
|
9
|
+
end
|
11
10
|
|
12
11
|
@list = Qt::ListWidget.new self
|
13
|
-
@list.connect(SIGNAL('itemDoubleClicked(QListWidgetItem*)'))
|
14
|
-
# x = ConsoleLauncher.new
|
15
|
-
#TODO: find sqlite binary
|
16
|
-
#x.run "/usr/bin/sqlite3 #{Dir.getwd}/#{$selected_app.cache_file item.full_path}"
|
17
|
-
|
12
|
+
@list.connect(SIGNAL('itemDoubleClicked(QListWidgetItem*)')) do |item|
|
18
13
|
cache_name = $selected_app.cache_file item.full_path
|
19
14
|
if cache_name.nil?
|
20
|
-
$log.error "File #{item.full_path} could not be downloaded. Either
|
15
|
+
$log.error "File #{item.full_path} could not be downloaded. Either" \
|
16
|
+
"the file does not exist (e.g., dead symlink) or there " \
|
17
|
+
"is a permission problem."
|
18
|
+
elsif RbConfig::CONFIG['host_os'] =~ /linux/
|
19
|
+
Process.spawn "'#{$settings['sqlite_editor']}' '#{cache_name}'"
|
21
20
|
else
|
22
|
-
|
23
|
-
Process.spawn "'#{$settings['sqlite_editor']}' '#{cache_name}'"
|
24
|
-
else
|
25
|
-
Process.spawn "open -a '#{$settings['sqlite_editor']}' '#{cache_name}'"
|
26
|
-
end
|
21
|
+
Process.spawn "open -a '#{$settings['sqlite_editor']}' '#{cache_name}'"
|
27
22
|
end
|
28
|
-
|
29
|
-
|
30
|
-
# "Launch app"
|
23
|
+
end
|
24
|
+
# "Launch app"
|
31
25
|
@default_protection = DefaultProtectionClassGroupWidget.new self
|
32
26
|
layout = Qt::VBoxLayout.new do |v|
|
33
27
|
v.add_widget(@default_protection)
|
@@ -55,22 +49,23 @@ module Idb
|
|
55
49
|
@list.setEnabled true
|
56
50
|
@default_protection.update
|
57
51
|
cache_dbs = $selected_app.find_cache_dbs
|
58
|
-
cache_dbs.each
|
52
|
+
cache_dbs.each do |full_path|
|
59
53
|
item = PathListWidgetItem.new
|
60
54
|
if $device.simulator?
|
61
|
-
item.setText full_path.sub($selected_app.app_dir,'')
|
55
|
+
item.setText full_path.sub($selected_app.app_dir, '')
|
62
56
|
else
|
63
57
|
pc = $device.protection_class full_path
|
64
58
|
if full_path.start_with? $selected_app.app_dir
|
65
|
-
|
59
|
+
stripped_app_dir_path = full_path.sub($selected_app.app_dir, '')
|
60
|
+
item.setText "[App Bundle]" + stripped_app_dir_path + " => " + pc.strip
|
66
61
|
elsif full_path.start_with? $selected_app.data_dir
|
67
|
-
|
62
|
+
stripped_data_dir_path = full_path.sub($selected_app.data_dir, '')
|
63
|
+
item.setText "[Data Dir]" + stripped_data_dir_path + " => " + pc.strip
|
68
64
|
end
|
69
65
|
end
|
70
66
|
item.full_path = full_path
|
71
67
|
@list.addItem item
|
72
|
-
|
68
|
+
end
|
73
69
|
end
|
74
|
-
|
75
70
|
end
|
76
|
-
end
|
71
|
+
end
|
data/lib/gui/certificate_item.rb
CHANGED
@@ -1,18 +1,16 @@
|
|
1
1
|
module Idb
|
2
|
-
|
3
2
|
class DefaultProtectionClassGroupWidget < Qt::GroupBox
|
4
|
-
|
5
|
-
|
6
|
-
super *args
|
3
|
+
def initialize(args)
|
4
|
+
super(*args)
|
7
5
|
|
8
6
|
setTitle "Default Data Protection"
|
9
7
|
|
10
8
|
@layout = Qt::GridLayout.new
|
11
|
-
label =
|
12
|
-
@val =
|
9
|
+
label = Qt::Label.new "<b>Default Data Protection</b>", self, 0
|
10
|
+
@val = Qt::Label.new "No default set in the entitlements of this app.", self, 0
|
13
11
|
@layout.addWidget label, 0, 0
|
14
12
|
@layout.addWidget @val, 0, 1
|
15
|
-
spacer_horizontal = Qt::SpacerItem.new 0,1, Qt::SizePolicy::Expanding, Qt::SizePolicy::Fixed
|
13
|
+
spacer_horizontal = Qt::SpacerItem.new 0, 1, Qt::SizePolicy::Expanding, Qt::SizePolicy::Fixed
|
16
14
|
@layout.addItem spacer_horizontal, 0, 2
|
17
15
|
|
18
16
|
setLayout @layout
|
@@ -22,15 +20,12 @@ module Idb
|
|
22
20
|
if $device.ios_version < 8
|
23
21
|
@val.setText "Only available for iOS 8+"
|
24
22
|
else
|
25
|
-
$selected_app.services_map.entitlements_by_bundle_id($selected_app.bundle_id).each
|
23
|
+
$selected_app.services_map.entitlements_by_bundle_id($selected_app.bundle_id).each do |x|
|
26
24
|
if x[0].to_s == "com.apple.developer.default-data-protection"
|
27
25
|
@val.setText x[1].to_s
|
28
26
|
end
|
29
|
-
|
27
|
+
end
|
30
28
|
end
|
31
|
-
|
32
29
|
end
|
33
|
-
|
34
30
|
end
|
35
31
|
end
|
36
|
-
|
@@ -5,60 +5,63 @@ module Idb
|
|
5
5
|
signals "disconnect()"
|
6
6
|
signals "connect_clicked()"
|
7
7
|
|
8
|
-
def initialize
|
9
|
-
super
|
8
|
+
def initialize(*args)
|
9
|
+
super(*args)
|
10
10
|
|
11
11
|
# details on selected app
|
12
12
|
@layout = Qt::GridLayout.new
|
13
13
|
setLayout(@layout)
|
14
14
|
setTitle "Selected Device"
|
15
15
|
|
16
|
-
|
17
|
-
|
16
|
+
device_label = "<b><font color='red'>Please select a device from the " \
|
17
|
+
"'Devices' menu or click 'Connect'.</font></b>"
|
18
|
+
@device = Qt::Label.new device_label, self, 0
|
19
|
+
@layout.addWidget @device, 0, 0, 2, 1
|
18
20
|
@connect = Qt::PushButton.new "Connect to USB/SSH device"
|
19
|
-
@connect.connect(SIGNAL(:released))
|
20
|
-
emit connect_clicked
|
21
|
-
|
22
|
-
@layout.addWidget @connect, 0,1,2,1
|
21
|
+
@connect.connect(SIGNAL(:released)) do
|
22
|
+
emit connect_clicked
|
23
|
+
end
|
24
|
+
@layout.addWidget @connect, 0, 1, 2, 1
|
23
25
|
end
|
24
26
|
|
25
27
|
def update_device
|
26
28
|
if $device.device?
|
27
29
|
@connect.hide
|
28
|
-
uname = $device.ops.execute("/bin/uname -a")
|
29
|
-
ssh_connection_info = ""
|
30
30
|
if $device.mode == "usb"
|
31
|
-
|
32
|
-
|
31
|
+
usb_mode_text = "<b>USB device:</b> Manually connect via SSH as " \
|
32
|
+
"#{$settings.ssh_username}@localhost:#{$device.usb_ssh_port}"
|
33
|
+
@device.setText usb_mode_text
|
33
34
|
else
|
34
|
-
|
35
|
+
ssh_mode_text = "<b>SSH device:</b> " \
|
36
|
+
"ssh://#{$settings.ssh_username}:[redacted]@" \
|
37
|
+
"#{$settings.ssh_host}:#{$settings.ssh_port}"
|
38
|
+
@device.setText ssh_mode_text
|
35
39
|
end
|
36
40
|
|
37
41
|
@status = Qt::PushButton.new "Status"
|
38
|
-
@status.connect(SIGNAL(:released))
|
42
|
+
@status.connect(SIGNAL(:released)) do
|
39
43
|
@device_status = DeviceStatusDialog.new
|
40
44
|
@device_status.exec
|
41
|
-
|
45
|
+
end
|
42
46
|
@layout.addWidget @status, 0, 1
|
43
47
|
|
44
48
|
@disconnect = Qt::PushButton.new "Disconnect"
|
45
|
-
@disconnect.connect(SIGNAL(:released))
|
49
|
+
@disconnect.connect(SIGNAL(:released)) do
|
46
50
|
$device.close unless $device.nil?
|
47
51
|
$device = nil
|
48
|
-
emit disconnect
|
52
|
+
emit disconnect
|
49
53
|
@disconnect.hide
|
50
54
|
@connect.show
|
51
55
|
@status.hide
|
52
|
-
|
53
|
-
|
56
|
+
device_selection_text = "<b><font color='red'>Please select a device " \
|
57
|
+
"from the 'Devices' menu.</font></b>"
|
58
|
+
@device.setText(device_selection_text)
|
59
|
+
end
|
54
60
|
@layout.addWidget @disconnect, 1, 1
|
55
61
|
|
56
|
-
|
57
62
|
else
|
58
63
|
@device.setText "<b>Simulator:</b> #{$device.sim_dir}"
|
59
64
|
end
|
60
65
|
end
|
61
|
-
|
62
|
-
|
63
66
|
end
|
64
|
-
end
|
67
|
+
end
|