You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ProfileManagerModel.java 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
  1. /*
  2. * Copyright (c) 2006-2014 DMDirc Developers
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy
  5. * of this software and associated documentation files (the "Software"), to deal
  6. * in the Software without restriction, including without limitation the rights
  7. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. * copies of the Software, and to permit persons to whom the Software is
  9. * furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  20. * SOFTWARE.
  21. */
  22. package com.dmdirc.addons.ui_swing.dialogs.profiles;
  23. import com.dmdirc.actions.wrappers.Profile;
  24. import com.dmdirc.interfaces.config.ConfigProvider;
  25. import com.dmdirc.interfaces.config.IdentityController;
  26. import com.dmdirc.interfaces.config.IdentityFactory;
  27. import com.dmdirc.util.validators.FileNameValidator;
  28. import com.dmdirc.util.validators.IdentValidator;
  29. import com.dmdirc.util.validators.ListNotEmptyValidator;
  30. import com.dmdirc.util.validators.NotEmptyValidator;
  31. import com.dmdirc.util.validators.ValidationResponse;
  32. import com.dmdirc.util.validators.Validator;
  33. import com.dmdirc.util.validators.ValidatorChain;
  34. import com.google.common.collect.ImmutableList;
  35. import java.beans.PropertyChangeListener;
  36. import java.beans.PropertyChangeListenerProxy;
  37. import java.beans.PropertyChangeSupport;
  38. import java.io.IOException;
  39. import java.util.ArrayList;
  40. import java.util.Collections;
  41. import java.util.List;
  42. /**
  43. * Model used to store state for the profile manager dialog.
  44. */
  45. public class ProfileManagerModel {
  46. /** Profile change support. */
  47. private final PropertyChangeSupport pcs;
  48. /** Identity Controller. */
  49. private final IdentityController identityController;
  50. /** Identity Factory. */
  51. private final IdentityFactory identityFactory;
  52. /** List of known profiles. */
  53. private List<Profile> profiles = new ArrayList<>();
  54. /** List of profiles to be displayed. */
  55. private final List<Profile> displayedProfiles = new ArrayList<>();
  56. /** Selected profile. */
  57. private Profile selectedProfile;
  58. /** Selected nickname. */
  59. private String selectedNickname;
  60. /**
  61. * Creates a new model.
  62. *
  63. * @param identityController Identity manager to retrieve profiles from.
  64. * @param identityFactory Factory to use when creating new profiles.
  65. */
  66. public ProfileManagerModel(
  67. final IdentityController identityController,
  68. final IdentityFactory identityFactory) {
  69. pcs = new PropertyChangeSupport(this);
  70. this.identityController = identityController;
  71. this.identityFactory = identityFactory;
  72. }
  73. /**
  74. * Load model with data.
  75. */
  76. public void load() {
  77. final List<ConfigProvider> identities = identityController.getProvidersByType("profile");
  78. for (ConfigProvider identity : identities) {
  79. profiles.add(new Profile(identityFactory, identity));
  80. }
  81. updateDisplayedProfiles();
  82. if (!profiles.isEmpty()) {
  83. selectedProfile = profiles.get(0);
  84. }
  85. final PropertyChangeListener[] listeners = pcs.getPropertyChangeListeners();
  86. for (PropertyChangeListener listener : listeners) {
  87. if (listener instanceof PropertyChangeListenerProxy) {
  88. final PropertyChangeListenerProxy proxy = (PropertyChangeListenerProxy) listener;
  89. pcs.firePropertyChange(proxy.getPropertyName(), null, null);
  90. }
  91. }
  92. }
  93. /**
  94. * Updates the list of displayed profiles, showing only those not marked deleted.
  95. */
  96. private void updateDisplayedProfiles() {
  97. displayedProfiles.clear();
  98. for (Profile profile : profiles) {
  99. if (!profile.isDeleted()) {
  100. displayedProfiles.add(profile);
  101. }
  102. }
  103. }
  104. /**
  105. * Gets the list of displayable profiles.
  106. *
  107. * @return List of profiles to display
  108. */
  109. public List<Profile> getProfiles() {
  110. return ImmutableList.copyOf(displayedProfiles);
  111. }
  112. /**
  113. * Sets the list of profiles.
  114. *
  115. * @param profiles List of profiles to display
  116. */
  117. public void setProfiles(final List<Profile> profiles) {
  118. this.profiles = new ArrayList<>(profiles);
  119. updateDisplayedProfiles();
  120. if (!profiles.contains(selectedProfile)) {
  121. upadateSelectedProfile(null);
  122. }
  123. if (selectedProfile == null && !profiles.isEmpty()) {
  124. upadateSelectedProfile(profiles.get(0));
  125. }
  126. pcs.firePropertyChange("profiles", null, null);
  127. }
  128. /**
  129. * Adds the specified profile to the list.
  130. *
  131. * @param profile New profile
  132. */
  133. public void addProfile(final Profile profile) {
  134. profiles.add(profile);
  135. updateDisplayedProfiles();
  136. upadateSelectedProfile(profile);
  137. pcs.firePropertyChange("profiles", null, null);
  138. }
  139. /**
  140. * Marks the selected profile as deleted.
  141. *
  142. * @param profile Profile to delete
  143. */
  144. public void deleteProfile(final Profile profile) {
  145. if (profile == null) {
  146. return;
  147. }
  148. profile.setDeleted(true);
  149. final int selected = displayedProfiles.indexOf(selectedProfile);
  150. displayedProfiles.remove(profile);
  151. final int size = displayedProfiles.size();
  152. Profile newSelectedProfile = null;
  153. if (profile != selectedProfile) {
  154. newSelectedProfile = selectedProfile;
  155. } else if (selected >= size && size > 0) {
  156. newSelectedProfile = displayedProfiles.get(size - 1);
  157. } else if (selected <= 0 && size > 0) {
  158. newSelectedProfile = displayedProfiles.get(0);
  159. } else if (selected - 1 >= 0 && size != 0) {
  160. newSelectedProfile = displayedProfiles.get(selected - 1);
  161. }
  162. upadateSelectedProfile(newSelectedProfile);
  163. pcs.firePropertyChange("profiles", null, null);
  164. }
  165. /**
  166. * Updates the selected profile. This method clears the selected nickname, the update method is
  167. * not called by this method.
  168. *
  169. * @param profile Newly selected profile, may be null
  170. */
  171. private void upadateSelectedProfile(final Profile profile) {
  172. selectedProfile = profile;
  173. selectedNickname = null;
  174. }
  175. /**
  176. * Sets the selected profile.
  177. *
  178. * @param selectedProfile Profile to select, null ignored
  179. */
  180. public void setSelectedProfile(final Object selectedProfile) {
  181. if (selectedProfile != null
  182. && !profiles.isEmpty()
  183. && profiles.contains(selectedProfile)) {
  184. upadateSelectedProfile((Profile) selectedProfile);
  185. }
  186. pcs.firePropertyChange("selectedprofile", null, null);
  187. }
  188. /**
  189. * Retrieves the selected profile.
  190. *
  191. * @return Selected profile (String)
  192. */
  193. public Object getSelectedProfile() {
  194. return selectedProfile;
  195. }
  196. /**
  197. * Retrieves the list of nicknames for the active profile, this will return an empty list if
  198. * there is no selected profile.
  199. *
  200. * @return List of nicknames
  201. */
  202. public List<String> getNicknames() {
  203. if (selectedProfile == null) {
  204. return Collections.emptyList();
  205. }
  206. return selectedProfile.getNicknames();
  207. }
  208. /**
  209. * Sets the list of nicknames for the active profile. Will do nothing if there is no active
  210. * profile.
  211. *
  212. * @param nicknames List of nicknames
  213. */
  214. public void setNicknames(final List<String> nicknames) {
  215. if (selectedProfile == null) {
  216. return;
  217. }
  218. selectedProfile.setNicknames(nicknames);
  219. pcs.firePropertyChange("nicknames", null, null);
  220. }
  221. /**
  222. * Adds the specified nickname to the list of nicknames in the active profile. Will do nothing
  223. * if there is no active profile.
  224. *
  225. * @param nickname New nickname
  226. */
  227. public void addNickname(final String nickname) {
  228. if (selectedProfile == null) {
  229. return;
  230. }
  231. selectedProfile.addNickname(nickname);
  232. selectedNickname = nickname;
  233. pcs.firePropertyChange("nicknames", null, null);
  234. }
  235. /**
  236. * Deletes the specified nickname from the active profile. This method will do nothing if there
  237. * is no active profile.
  238. *
  239. * @param nickname Nickname to be deleted (This method will do nothing if this is not a String)
  240. */
  241. public void deleteNickname(final Object nickname) {
  242. if (nickname instanceof String) {
  243. deleteNickname((String) nickname);
  244. }
  245. }
  246. /**
  247. * Deletes the specified nickname from the active profile. This method will do nothing if there
  248. * is no active profile.
  249. *
  250. * @param nickname Nickname to be deleted
  251. */
  252. public void deleteNickname(final String nickname) {
  253. if (selectedProfile == null || nickname == null) {
  254. return;
  255. }
  256. final int selected = selectedProfile.getNicknames().indexOf(nickname);
  257. selectedProfile.delNickname(nickname);
  258. final int size = selectedProfile.getNicknames().size();
  259. String newSelectedNickname = null;
  260. if (!nickname.equals(selectedNickname)) {
  261. newSelectedNickname = selectedNickname;
  262. } else if (selected >= size && size > 0) {
  263. newSelectedNickname = selectedProfile.getNicknames().get(size - 1);
  264. } else if (selected <= 0 && size > 0) {
  265. newSelectedNickname = selectedProfile.getNicknames().get(0);
  266. } else if (selected - 1 >= 0 && size != 0) {
  267. newSelectedNickname = selectedProfile.getNicknames().get(selected - 1);
  268. }
  269. selectedNickname = newSelectedNickname;
  270. pcs.firePropertyChange("nicknames", null, null);
  271. }
  272. /**
  273. * Alters the specified nickname.
  274. *
  275. * @param nickname Nickname to be edited
  276. * @param edited Resultant nickname
  277. */
  278. public void editNickname(final String nickname, final String edited) {
  279. selectedNickname = edited;
  280. selectedProfile.editNickname(nickname, edited);
  281. selectedNickname = edited;
  282. pcs.firePropertyChange("nicknames", null, null);
  283. }
  284. /**
  285. * Sets the selected nickname on the active profile. This method expects a String, it will do
  286. * nothing if the parameter is not. If the specified nickname is not found the selection will be
  287. * cleared.
  288. *
  289. * @param selectedNickname Nickname to be selected, may be null. (This method will do nothing if
  290. * this is not a String)
  291. */
  292. public void setSelectedNickname(final Object selectedNickname) {
  293. if (selectedProfile != null
  294. && selectedNickname instanceof String
  295. && selectedProfile.getNicknames().contains(selectedNickname)) {
  296. this.selectedNickname = (String) selectedNickname;
  297. pcs.firePropertyChange("selectednickname", null, null);
  298. }
  299. }
  300. /**
  301. * Retrieves the selected nickname from the active profile.
  302. *
  303. * @return Selected nickname (String) or an empty string if there is no active profile
  304. */
  305. public Object getSelectedNickname() {
  306. if (selectedProfile == null) {
  307. return "";
  308. }
  309. return selectedNickname;
  310. }
  311. /**
  312. * Retrieves the name of the active profile.
  313. *
  314. * @return Active profile name or an empty string if there is no active profile
  315. */
  316. public String getName() {
  317. if (selectedProfile == null) {
  318. return "";
  319. }
  320. return selectedProfile.getName();
  321. }
  322. /**
  323. * Sets the name of the active profile. This method will do nothing if there is no active
  324. * profile.
  325. *
  326. * @param name New profile name
  327. */
  328. public void setName(final String name) {
  329. if (selectedProfile != null) {
  330. selectedProfile.setName(name);
  331. pcs.firePropertyChange("name", null, null);
  332. }
  333. }
  334. /**
  335. * Retrieves the realname in the active profile.
  336. *
  337. * @return Active profile realname or an empty string if there is no active profile
  338. */
  339. public String getRealname() {
  340. if (selectedProfile == null) {
  341. return "";
  342. }
  343. return selectedProfile.getRealname();
  344. }
  345. /**
  346. * Sets the realname in the active profile. This method will do nothing if there is no active
  347. * profile.
  348. *
  349. * @param realname New profile real name
  350. */
  351. public void setRealname(final String realname) {
  352. if (selectedProfile != null) {
  353. selectedProfile.setRealname(realname);
  354. pcs.firePropertyChange("realname", null, null);
  355. }
  356. }
  357. /**
  358. * Retrieves the ident of the active profile. This method will return an empty string if there
  359. * is no active profile.
  360. *
  361. * @return Active profile ident or an empty string if there is no active profile
  362. */
  363. public String getIdent() {
  364. if (selectedProfile == null) {
  365. return "";
  366. }
  367. return selectedProfile.getIdent();
  368. }
  369. /**
  370. * Sets the ident of the active profile. This method will do nothing if there is no active
  371. * profile.
  372. *
  373. * @param ident New profile ident
  374. */
  375. public void setIdent(final String ident) {
  376. if (selectedProfile != null) {
  377. selectedProfile.setIdent(ident);
  378. pcs.firePropertyChange("ident", null, null);
  379. }
  380. }
  381. /**
  382. * Checks whether is it possible to manipulate a profile.
  383. *
  384. * @return true when a profile is selected
  385. */
  386. public boolean isManipulateProfileAllowed() {
  387. return getSelectedProfile() != null;
  388. }
  389. /**
  390. * Checks whether is it possible to manipulate a nickname.
  391. *
  392. * @return true when a nickname is selected
  393. */
  394. public boolean isManipulateNicknameAllowed() {
  395. return getSelectedProfile() != null && getSelectedNickname() != null;
  396. }
  397. /**
  398. * Is it possible to change profile?
  399. *
  400. * @return true if all validators for the selected profile pass
  401. */
  402. public boolean isChangeProfileAllowed() {
  403. return !isNameValid().isFailure()
  404. && !isNicknamesValid().isFailure()
  405. && !isIdentValid().isFailure()
  406. && !isRealnameValid().isFailure();
  407. }
  408. /**
  409. * Is it possible to save and close the dialog?
  410. *
  411. * @return true if all other validators pass and there is at least one profile
  412. */
  413. public boolean isOKAllowed() {
  414. return !profiles.isEmpty()
  415. && !isNameValid().isFailure()
  416. && !isNicknamesValid().isFailure()
  417. && !isIdentValid().isFailure()
  418. && !isRealnameValid().isFailure();
  419. }
  420. /**
  421. * Retrieves the profile name validator.
  422. *
  423. * @return Passes if the name is a non empty non duplicate filename
  424. */
  425. public Validator<String> getNameValidator() {
  426. return ValidatorChain.<String>builder()
  427. .addValidator(new FileNameValidator())
  428. .addValidator(new ProfileRenameValidator(this))
  429. .build();
  430. }
  431. /**
  432. * Is the profile name valid? If there is no active profile the validation passes.
  433. *
  434. * @return Passes if the name is a non empty non duplicate filename
  435. */
  436. public ValidationResponse isNameValid() {
  437. if (selectedProfile == null) {
  438. return new ValidationResponse();
  439. }
  440. return getNameValidator().validate(selectedProfile.getName());
  441. }
  442. /**
  443. * Are the nicknames in the active profile valid? If there is no active profile the validation
  444. * passes.
  445. *
  446. * @return passes when there are nicknames present
  447. */
  448. public ValidationResponse isNicknamesValid() {
  449. if (selectedProfile == null) {
  450. return new ValidationResponse();
  451. }
  452. return getNicknamesValidator().validate(selectedProfile.getNicknames());
  453. }
  454. /**
  455. * Retrieves the nicknames validator.
  456. *
  457. * @return Passes if the nicknames list is non empty
  458. */
  459. public Validator<List<String>> getNicknamesValidator() {
  460. return new ListNotEmptyValidator<>();
  461. }
  462. /**
  463. * Retrieves the realname validator.
  464. *
  465. * @return Passes if the realname is a non empty string
  466. */
  467. public Validator<String> getRealnameValidator() {
  468. return new NotEmptyValidator();
  469. }
  470. /**
  471. * Is the realname in the active profile valid? If there is no active profile the validation
  472. * passes.
  473. *
  474. * @return passes the realname is valid
  475. */
  476. public ValidationResponse isRealnameValid() {
  477. if (selectedProfile == null) {
  478. return new ValidationResponse();
  479. }
  480. return getRealnameValidator().validate(selectedProfile.getRealname());
  481. }
  482. /**
  483. * Retrieves the ident validator.
  484. *
  485. * @return Passes if the ident is an empty string and a valid ident
  486. */
  487. public Validator<String> getIdentValidator() {
  488. return ValidatorChain.<String>builder()
  489. .addValidator(new IdentValidator())
  490. .build();
  491. }
  492. /**
  493. * Is the ident in the active profile valid? If there is no active profile the validation
  494. * passes.
  495. *
  496. * @return passes the ident is valid
  497. */
  498. public ValidationResponse isIdentValid() {
  499. if (selectedProfile == null) {
  500. return new ValidationResponse();
  501. }
  502. return getIdentValidator().validate(selectedProfile.getIdent());
  503. }
  504. /**
  505. * This method saves any changes made in the model to disk. All profiles marked for deletion are
  506. * removed and then all remaining profiles are have their save method called.
  507. */
  508. public void save() {
  509. for (Profile profile : profiles) {
  510. if (profile.isDeleted()) {
  511. try {
  512. profile.delete();
  513. } catch (IOException ex) {
  514. // TODO: handle somehow
  515. }
  516. } else {
  517. profile.save();
  518. }
  519. }
  520. }
  521. /**
  522. * Adds a property change listener to all property change events in the model.
  523. *
  524. * @param listener Listener to add
  525. */
  526. public void addPropertyChangeListener(final PropertyChangeListener listener) {
  527. pcs.addPropertyChangeListener(listener);
  528. }
  529. /**
  530. * Adds a property change listener to a given property on this model.
  531. *
  532. * @param propertyName Property to listen on
  533. * @param listener Listener to add
  534. */
  535. public void addPropertyChangeListener(final String propertyName,
  536. final PropertyChangeListener listener) {
  537. pcs.addPropertyChangeListener(propertyName, listener);
  538. }
  539. }