2929import javafx .scene .control .TableView ;
3030import javafx .scene .layout .BorderPane ;
3131import javafx .scene .layout .HBox ;
32- import org .jackhuang .hmcl .mod .LocalModFile ;
33- import org .jackhuang .hmcl .mod .ModManager ;
32+ import org .jackhuang .hmcl .mod .LocalAddonFile ;
33+ import org .jackhuang .hmcl .mod .LocalAddonManager ;
3434import org .jackhuang .hmcl .mod .RemoteMod ;
3535import org .jackhuang .hmcl .task .FileDownloadTask ;
3636import org .jackhuang .hmcl .task .Schedulers ;
4141import org .jackhuang .hmcl .ui .construct .MessageDialogPane ;
4242import org .jackhuang .hmcl .ui .construct .PageCloseEvent ;
4343import org .jackhuang .hmcl .ui .decorator .DecoratorPage ;
44- import org .jackhuang .hmcl .util .Pair ;
44+ import org .jackhuang .hmcl .util .StringUtils ;
4545import org .jackhuang .hmcl .util .TaskCancellationAction ;
4646import org .jackhuang .hmcl .util .io .CSVTable ;
4747
48+ import java .io .IOException ;
4849import java .nio .file .Path ;
4950import java .nio .file .Paths ;
5051import java .time .LocalDateTime ;
5657import java .util .stream .Collectors ;
5758
5859import static org .jackhuang .hmcl .ui .FXUtils .onEscPressed ;
59- import static org .jackhuang .hmcl .util .Pair .pair ;
6060import static org .jackhuang .hmcl .util .i18n .I18n .i18n ;
61+ import static org .jackhuang .hmcl .util .logging .Logger .LOG ;
6162
62- public class ModUpdatesPage extends BorderPane implements DecoratorPage {
63+ public class AddonUpdatesPage < F extends LocalAddonFile > extends BorderPane implements DecoratorPage {
6364 private final ReadOnlyObjectWrapper <State > state = new ReadOnlyObjectWrapper <>(DecoratorPage .State .fromTitle (i18n ("mods.check_updates" )));
6465
65- private final ModManager modManager ;
66- private final ObservableList <ModUpdateObject > objects ;
66+ private final LocalAddonManager < F > localAddonManager ;
67+ private final ObservableList <AddonUpdateObject > objects ;
6768
6869 @ SuppressWarnings ("unchecked" )
69- public ModUpdatesPage ( ModManager modManager , List <LocalModFile . ModUpdate > updates ) {
70- this .modManager = modManager ;
70+ public AddonUpdatesPage ( LocalAddonManager < F > localAddonManager , List <LocalAddonFile . AddonUpdate > updates ) {
71+ this .localAddonManager = localAddonManager ;
7172
7273 getStyleClass ().add ("gray-background" );
7374
74- TableColumn <ModUpdateObject , Boolean > enabledColumn = new TableColumn <>();
75+ TableColumn <AddonUpdateObject , Boolean > enabledColumn = new TableColumn <>();
7576 var allEnabledBox = new JFXCheckBox ();
7677 enabledColumn .setStyle ("-fx-alignment: CENTER;" );
7778 enabledColumn .setGraphic (allEnabledBox );
7879 enabledColumn .setCellFactory (JFXCheckBoxTableCell .forTableColumn (enabledColumn ));
79- setupCellValueFactory (enabledColumn , ModUpdateObject ::enabledProperty );
80+ setupCellValueFactory (enabledColumn , AddonUpdateObject ::enabledProperty );
8081 enabledColumn .setEditable (true );
8182 enabledColumn .setMaxWidth (40 );
8283 enabledColumn .setMinWidth (40 );
8384
84- TableColumn <ModUpdateObject , String > fileNameColumn = new TableColumn <>(i18n ("mods.check_updates.file" ));
85+ TableColumn <AddonUpdateObject , String > fileNameColumn = new TableColumn <>(i18n ("mods.check_updates.file" ));
8586 fileNameColumn .setPrefWidth (200 );
86- setupCellValueFactory (fileNameColumn , ModUpdateObject ::fileNameProperty );
87+ setupCellValueFactory (fileNameColumn , AddonUpdateObject ::fileNameProperty );
8788
88- TableColumn <ModUpdateObject , String > currentVersionColumn = new TableColumn <>(i18n ("mods.check_updates.current_version" ));
89+ TableColumn <AddonUpdateObject , String > currentVersionColumn = new TableColumn <>(i18n ("mods.check_updates.current_version" ));
8990 currentVersionColumn .setPrefWidth (200 );
90- setupCellValueFactory (currentVersionColumn , ModUpdateObject ::currentVersionProperty );
91+ setupCellValueFactory (currentVersionColumn , AddonUpdateObject ::currentVersionProperty );
9192
92- TableColumn <ModUpdateObject , String > targetVersionColumn = new TableColumn <>(i18n ("mods.check_updates.target_version" ));
93+ TableColumn <AddonUpdateObject , String > targetVersionColumn = new TableColumn <>(i18n ("mods.check_updates.target_version" ));
9394 targetVersionColumn .setPrefWidth (200 );
94- setupCellValueFactory (targetVersionColumn , ModUpdateObject ::targetVersionProperty );
95+ setupCellValueFactory (targetVersionColumn , AddonUpdateObject ::targetVersionProperty );
9596
96- TableColumn <ModUpdateObject , String > sourceColumn = new TableColumn <>(i18n ("mods.check_updates.source" ));
97- setupCellValueFactory (sourceColumn , ModUpdateObject ::sourceProperty );
97+ TableColumn <AddonUpdateObject , String > sourceColumn = new TableColumn <>(i18n ("mods.check_updates.source" ));
98+ setupCellValueFactory (sourceColumn , AddonUpdateObject ::sourceProperty );
9899
99- objects = FXCollections .observableList (updates .stream ().map (ModUpdateObject ::new ).collect (Collectors .toList ()));
100+ objects = FXCollections .observableList (updates .stream ().map (AddonUpdateObject ::new ).collect (Collectors .toList ()));
100101 FXUtils .bindAllEnabled (allEnabledBox .selectedProperty (), objects .stream ().map (o -> o .enabled ).toArray (BooleanProperty []::new ));
101102
102- TableView <ModUpdateObject > table = new TableView <>(objects );
103+ TableView <AddonUpdateObject > table = new TableView <>(objects );
103104 table .setEditable (true );
104105 table .getColumns ().setAll (enabledColumn , fileNameColumn , currentVersionColumn , targetVersionColumn , sourceColumn );
105106 setMargin (table , new Insets (10 , 10 , 5 , 10 ));
@@ -114,7 +115,7 @@ public ModUpdatesPage(ModManager modManager, List<LocalModFile.ModUpdate> update
114115 exportListButton .setOnAction (e -> exportList ());
115116
116117 JFXButton nextButton = FXUtils .newRaisedButton (i18n ("mods.check_updates.confirm" ));
117- nextButton .setOnAction (e -> updateMods ());
118+ nextButton .setOnAction (e -> updateFiles ());
118119
119120 JFXButton cancelButton = FXUtils .newRaisedButton (i18n ("button.cancel" ));
120121 cancelButton .setOnAction (e -> fireEvent (new PageCloseEvent ()));
@@ -125,23 +126,24 @@ public ModUpdatesPage(ModManager modManager, List<LocalModFile.ModUpdate> update
125126 setBottom (actions );
126127 }
127128
128- private <T > void setupCellValueFactory (TableColumn <ModUpdateObject , T > column , Function <ModUpdateObject , ObservableValue <T >> mapper ) {
129+ private <T > void setupCellValueFactory (TableColumn <AddonUpdateObject , T > column , Function <AddonUpdateObject , ObservableValue <T >> mapper ) {
129130 column .setCellValueFactory (param -> mapper .apply (param .getValue ()));
130131 }
131132
132- private void updateMods () {
133- ModUpdateTask task = new ModUpdateTask (
134- modManager ,
133+ private void updateFiles () {
134+ AddonUpdateTask task = new AddonUpdateTask (
135+ localAddonManager . getDirectory () ,
135136 objects .stream ()
136- .filter (o -> o .enabled .get ())
137- .map (object -> pair (object .data .getLocalMod (), object .data .getCandidate ()))
138- .collect (Collectors .toList ()));
137+ .filter (AddonUpdateObject ::isEnabled )
138+ .map (AddonUpdateObject ::getData )
139+ .toList ()
140+ );
139141 Controllers .taskDialog (
140142 task .whenComplete (Schedulers .javafx (), exception -> {
141143 fireEvent (new PageCloseEvent ());
142- if (!task .getFailedMods ().isEmpty ()) {
144+ if (!task .getFailedAddons ().isEmpty ()) {
143145 Controllers .dialog (i18n ("mods.check_updates.failed_download" ) + "\n " +
144- task .getFailedMods ().stream ().map (LocalModFile ::getFileName ).collect (Collectors .joining ("\n " )),
146+ task .getFailedAddons ().stream ().map (LocalAddonFile ::getFileName ).collect (Collectors .joining ("\n " )),
145147 i18n ("install.failed" ),
146148 MessageDialogPane .MessageType .ERROR );
147149 }
@@ -189,22 +191,22 @@ public ReadOnlyObjectWrapper<State> stateProperty() {
189191 return state ;
190192 }
191193
192- private static final class ModUpdateObject {
193- final LocalModFile . ModUpdate data ;
194+ private static final class AddonUpdateObject {
195+ final LocalAddonFile . AddonUpdate data ;
194196 final BooleanProperty enabled = new SimpleBooleanProperty ();
195197 final StringProperty fileName = new SimpleStringProperty ();
196198 final StringProperty currentVersion = new SimpleStringProperty ();
197199 final StringProperty targetVersion = new SimpleStringProperty ();
198200 final StringProperty source = new SimpleStringProperty ();
199201
200- public ModUpdateObject ( LocalModFile . ModUpdate data ) {
202+ public AddonUpdateObject ( LocalAddonFile . AddonUpdate data ) {
201203 this .data = data ;
202204
203- enabled .set (!data .getLocalMod ().getModManager (). isDisabled (data . getLocalMod (). getFile () ));
204- fileName .set (data .getLocalMod ().getFileName ());
205- currentVersion .set (data .getCurrentVersion ().getVersion ());
206- targetVersion .set (data .getCandidate ().getVersion ());
207- switch (data .getCurrentVersion ().getSelf ().getType ()) {
205+ enabled .set (!data .localAddonFile ().isDisabled ());
206+ fileName .set (data .localAddonFile ().getFileName ());
207+ currentVersion .set (data .currentVersion ().getVersion ());
208+ targetVersion .set (data .targetVersion ().getVersion ());
209+ switch (data .currentVersion ().getSelf ().getType ()) {
208210 case CURSEFORGE :
209211 source .set (i18n ("mods.curseforge" ));
210212 break ;
@@ -213,6 +215,10 @@ public ModUpdateObject(LocalModFile.ModUpdate data) {
213215 }
214216 }
215217
218+ public LocalAddonFile .AddonUpdate getData () {
219+ return data ;
220+ }
221+
216222 public boolean isEnabled () {
217223 return enabled .get ();
218224 }
@@ -274,30 +280,32 @@ public void setSource(String source) {
274280 }
275281 }
276282
277- public static class ModUpdateTask extends Task <Void > {
283+ public static class AddonUpdateTask extends Task <Void > {
278284 private final Collection <Task <?>> dependents ;
279- private final List <LocalModFile > failedMods = new ArrayList <>();
285+ private final List <LocalAddonFile > failedAddons = new ArrayList <>();
280286
281- ModUpdateTask ( ModManager modManager , List <Pair < LocalModFile , RemoteMod . Version >> mods ) {
287+ AddonUpdateTask ( Path addonDirectory , List <LocalAddonFile . AddonUpdate > addons ) {
282288 setStage ("mods.check_updates.confirm" );
283- getProperties ().put ("total" , mods .size ());
289+ getProperties ().put ("total" , addons .size ());
284290
285291 this .dependents = new ArrayList <>();
286- for (Pair <LocalModFile , RemoteMod .Version > mod : mods ) {
287- LocalModFile local = mod .getKey ();
288- RemoteMod .Version remote = mod .getValue ();
289- boolean isDisabled = local .getModManager ().isDisabled (local .getFile ());
292+ for (LocalAddonFile .AddonUpdate addon : addons ) {
293+ LocalAddonFile local = addon .localAddonFile ();
294+ RemoteMod .Version remote = addon .targetVersion ();
295+ boolean isDisabled = local .isDisabled ();
296+ String originalFileName = local .getFile ().getFileName ().toString ();
290297
291298 dependents .add (Task
292299 .runAsync (Schedulers .javafx (), () -> local .setOld (true ))
293300 .thenComposeAsync (() -> {
294- String fileName = remote .getFile ().getFilename ();
301+ String fileName = addon . useRemoteFileName () ? remote .getFile ().getFilename () : originalFileName ;
295302 if (isDisabled )
296- fileName += ModManager . DISABLED_EXTENSION ;
303+ fileName = StringUtils . addSuffix ( fileName , LocalAddonManager . DISABLED_EXTENSION ) ;
297304
298305 var task = new FileDownloadTask (
299306 remote .getFile ().getUrl (),
300- modManager .getModsDirectory ().resolve (fileName ));
307+ addonDirectory .resolve (fileName )
308+ );
301309
302310 task .setName (remote .getName ());
303311 return task ;
@@ -307,16 +315,22 @@ public static class ModUpdateTask extends Task<Void> {
307315 // restore state if failed
308316 local .setOld (false );
309317 if (isDisabled )
310- local .disable ();
311- failedMods .add (local );
318+ local .markDisabled ();
319+ failedAddons .add (local );
320+ } else if (!local .keepOldFiles ()) {
321+ try {
322+ local .delete ();
323+ } catch (IOException e ) {
324+ LOG .warning ("Failed to delete outdated addon: " + local .getFile (), e );
325+ }
312326 }
313327 })
314328 .withCounter ("mods.check_updates.confirm" ));
315329 }
316330 }
317331
318- public List <LocalModFile > getFailedMods () {
319- return failedMods ;
332+ public List <LocalAddonFile > getFailedAddons () {
333+ return failedAddons ;
320334 }
321335
322336 @ Override
0 commit comments