-
Notifications
You must be signed in to change notification settings - Fork 19
/
addon_selection_base_dialog.rb
331 lines (276 loc) · 10.7 KB
/
addon_selection_base_dialog.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
# Copyright (c) 2015 SUSE LINUX GmbH, Nuernberg, Germany.
require "yast"
require "registration/ui/abort_confirmation"
require "registration/addon"
require "registration/addon_sorter"
require "registration/sw_mgmt"
module Registration
module UI
# this class displays and runs the dialog with addon selection
class AddonSelectionBaseDialog
include Yast::Logger
include Yast::I18n
include Yast::UIShortcuts
include Yast
Yast.import "Mode"
Yast.import "GetInstArgs"
Yast.import "Popup"
Yast.import "Report"
Yast.import "UI"
Yast.import "Wizard"
Yast.import "Stage"
Yast.import "Arch"
# constructor
# @param registration [Registration::Registration] use this Registration object for
# communication with SCC
def initialize(registration)
textdomain "registration"
@addons = Addon.find_all(registration)
# sort the addons
@addons.sort!(&::Registration::ADDON_SORTER)
@old_selection = Addon.selected.dup
# activate a workaround on ARM (FATE#320679)
aarch64_workaround if Arch.aarch64
log.info "Available addons: #{@addons}"
end
# reimplement this in a subclass
# display the extension selection dialog and wait for a button click
# @return [Symbol] user input
def run
raise "Not implemented"
end
protected
# create widget ID for an addon
# @param [<Addon>] addon the addon
# @return [String] widget id
def addon_widget_id(addon)
"#{addon.identifier}-#{addon.version}-#{addon.arch}"
end
private
# reimplement this in a subclass
# @return [String] dialog head
def heading
raise "Not implemented"
end
# reimplement this in a subclass
# @return [Boolean] is the addon selected?
def addon_selected?(_addon)
raise "Not implemented"
end
# create the main dialog definition
# @return [Yast::Term] the main UI dialog term
def content
VBox(
Left(Heading(heading)),
addons_box,
Left(Label(_("Details"))),
details_widget
)
end
# addon description widget
# @return [Yast::Term] the addon details widget
def details_widget
MinHeight(8,
VWeight(25, RichText(Id(:details), Opt(:disabled), "<small>" +
_("Select an extension or a module to show details here") + "</small>")))
end
# @return [String] a Value for a RichText
def richtext_checkboxes(addons)
items = addons.map do |addon|
# checkbox label for an unavailable extension
# (%s is an extension name)
label = addon.available? ? addon.label : (_("%s (not available)") % addon.label)
rt_cb(id: addon_widget_id(addon),
label: label,
selected: addon_selected?(addon),
enabled: addon.available?,
indented: addon.depends_on)
end
items.join("\n")
end
# FIXME: acknowledge Lada's code
# https://gist.github.com/lslezak/5ed82fe19337bef4807b
# FIXME: adapt to installation theme
IMG_ON = "/usr/share/YaST2/theme/current/wizard/checkbox-on.png"
IMG_OFF = "/usr/share/YaST2/theme/current/wizard/checkbox-off.png"
def rt_cb(id:, label:, selected:, enabled:,
indented: false, text_mode: Yast::UI.TextMode)
selected = false unless enabled
if text_mode
indent = " " * (indented ? 5 : 1)
check = selected ? "[x]" : "[ ]"
widget = "#{check} #{label}"
enabled_widget = enabled ? "<a href=\"#{id}\">#{widget}</a>" : widget
"#{indent}#{enabled_widget}<br>"
else
indent = " " * (indented ? 7 : 1)
# FIXME: a disabled checkbox?
check = "<img src='#{selected ? IMG_ON : IMG_OFF}'></img>"
widget = "#{check} #{label}"
enabled_widget = if enabled
# FIXME: adapt to installation theme: find a suitable class?
"<a href='#{id}' style='text-decoration:none; color:black'>#{widget}</a>"
else
"<span style='color:grey'>#{widget}</span>"
end
"<p>#{indent}#{enabled_widget}</p>"
end
end
# create UI box with addon check boxes, if the number of the addons is too big
# the UI uses two column layout
# @return [Yast::Term] the main UI dialog term
def addons_box
content = RichText(Id(:items), richtext_checkboxes(@addons))
VWeight(75, MinHeight(12, content))
end
# display the addon checkboxes in two columns
# @param col1 [Array<Addon>] the addons displayed in the first column
# @param col2 [Array<Addon>] the addons displayed in the second column
# @return [Yast::Term] the addon cheboxes
def two_column_layout(col1, col2)
box2 = addon_selection_items(col1)
box2.params << VStretch() # just UI tweak
HBox(
addon_selection_items(col2),
HSpacing(1),
box2
)
end
# create a single UI column with addon checkboxes
# @return [Yast::Term] addon column
def addon_selection_items(addons)
box = VBox()
# whether to add extra spacing in the UI
add_extra_spacing = if Yast::UI.TextMode
addons.size < 5
else
true
end
addons.each do |addon|
box.params.concat(addon_checkbox(addon, add_extra_spacing))
end
box
end
# create spacing around the addon checkbox so the layout looks better
# @param addon [Registration::Addon]
# @param extra_spacing [Boolean] add extra spacing (indicates enough space in UI)
# @return [Array<Yast::Term>] Return array with one or two elements for VBox
def addon_checkbox(addon, extra_spacing)
checkbox = Left(addon_checkbox_element(addon))
# usability help. If addon depends on something, then we get it
# immediatelly after parent, so indent it slightly, so it is easier visible
checkbox = HBox(HSpacing(2.5), checkbox) if addon.depends_on
res = [checkbox]
# add extra spacing when there are just few addons, in GUI always
res << VSpacing(0.7) if extra_spacing
res
end
# create the UI checkbox element for the addon
# @param addon [Registration::Addon] the addon
# @return [Yast::Term] checkbox term
def addon_checkbox_element(addon)
# checkbox label for an unavailable extension
# (%s is an extension name)
label = addon.available? ? addon.label : (_("%s (not available)") % addon.label)
CheckBox(Id(addon_widget_id(addon)), Opt(:notify), label, addon_selected?(addon))
end
# the main event loop - handle the user in put in the dialog
# @return [Symbol] the user input
def handle_dialog
Yast::UI.SetFocus(Id(:items))
ret = nil
continue_buttons = [:next, :back, :abort, :skip]
until continue_buttons.include?(ret)
ret = Yast::UI.UserInput
case ret
when :next
ret = handle_next_button
when :cancel, :abort
ret = Stage.initial && !AbortConfirmation.run ? nil : :abort
# when canceled switch to old selection
Addon.selected.replace(@old_selection) if ret == :abort
else
handle_addon_selection(ret)
end
end
ret
end
# handler for the :next button in the main loop
def handle_next_button
return nil unless supported_addon_count?
log.info "Selected addons: #{Addon.selected.map(&:name)}"
Addon.selected.empty? ? :skip : :next
end
# handler for changing the addon status in the main loop
# @param id [String] addon widget id
def handle_addon_selection(id)
# check whether it's an add-on ID (checkbox clicked)
addon = @addons.find { |a| addon_widget_id(a) == id }
return unless addon
show_addon_details(addon)
new_state = ! addon.selected?
if new_state
addon.selected
else
addon.unselected
end
reactivate_dependencies
end
# update addon details after changing the current addon in the UI
# @param addon []
def show_addon_details(addon)
# addon description is a rich text
Yast::UI.ChangeWidget(Id(:details), :Value, addon.description)
Yast::UI.ChangeWidget(Id(:details), :Enabled, true)
end
# update the enabled/disabled status in UI for dependent addons
# FIXME: is this always overriden?
def reactivate_dependencies
Yast::UI.ChangeWidget(Id(:items), :Value, richtext_checkboxes(@addons))
end
# the maximum number of reg. codes displayed vertically,
# this is the limit for 80x25 textmode UI
MAX_REGCODES_PER_COLUMN = 8
# check the number of required reg. codes
# @return [Boolean] true if the number of the required reg. codes fits
# the maximum limit
def supported_addon_count?
# maximum number or reg codes which can be displayed in two column layout
max_supported = 2 * MAX_REGCODES_PER_COLUMN
# check if the count of addons requiring a reg. code fits two columns
if Addon.selected.count { |a| a.registered? && a.free } > max_supported
Report.Error(_("YaST allows to select at most %s extensions or modules.") % max_supported)
return false
end
true
end
# shared part of the help text
# @return [String] translated help text
def generic_help_text
# help text (2/3)
_("<p>Please note, that some extensions or modules might need "\
"specific registration code.</p>") +
# help text (3/3)
_("<p>If you want to remove any extension or module you need to log"\
"into the SUSE Customer Center and remove them manually there.</p>")
end
# workaround for FATE#320679 - preselect the Toolchain module on ARM
# in SLES12-SP2
# FIXME: remove this hack in SLES12-SP3, use a proper solution instead
def aarch64_workaround
# SLES12-SP2 base?
product = SwMgmt.base_product_to_register
return unless product["name"] == "SLES" && product["version"] == "12.2"
# is the Toolchain module available?
toolchain = @addons.find do |addon|
addon.identifier == "sle-module-toolchain" && addon.version == "12" \
&& addon.arch == "aarch64"
end
return unless toolchain
# then pre-select it!
log.info "Activating the ARM64 workaround, preselecting addon: #{toolchain}"
toolchain.selected
end
end
end
end