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.

SwingPreferencesDialog.java 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. /*
  2. * Copyright (c) 2006-2017 DMDirc Developers
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
  5. * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
  6. * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
  7. * permit persons to whom the Software is furnished to do so, subject to the following conditions:
  8. *
  9. * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
  10. * Software.
  11. *
  12. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  13. * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
  14. * OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  15. * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  16. */
  17. package com.dmdirc.addons.ui_swing.dialogs.prefs;
  18. import com.dmdirc.addons.ui_swing.UIUtilities;
  19. import com.dmdirc.addons.ui_swing.components.IconManager;
  20. import com.dmdirc.addons.ui_swing.components.ListScroller;
  21. import com.dmdirc.addons.ui_swing.components.SupplierLoggingSwingWorker;
  22. import com.dmdirc.addons.ui_swing.dialogs.StandardDialog;
  23. import com.dmdirc.addons.ui_swing.dialogs.updater.SwingRestartDialog;
  24. import com.dmdirc.addons.ui_swing.injection.DialogModule.ForSettings;
  25. import com.dmdirc.addons.ui_swing.injection.DialogProvider;
  26. import com.dmdirc.addons.ui_swing.injection.MainWindow;
  27. import com.dmdirc.config.prefs.PreferencesCategory;
  28. import com.dmdirc.config.prefs.PreferencesDialogModel;
  29. import java.awt.Window;
  30. import java.awt.event.ActionEvent;
  31. import java.awt.event.ActionListener;
  32. import javax.inject.Inject;
  33. import javax.inject.Provider;
  34. import javax.swing.BorderFactory;
  35. import javax.swing.DefaultListModel;
  36. import javax.swing.JButton;
  37. import javax.swing.JList;
  38. import javax.swing.JScrollPane;
  39. import javax.swing.ListSelectionModel;
  40. import javax.swing.ScrollPaneConstants;
  41. import javax.swing.SwingWorker;
  42. import javax.swing.WindowConstants;
  43. import javax.swing.event.ListSelectionEvent;
  44. import javax.swing.event.ListSelectionListener;
  45. import net.miginfocom.swing.MigLayout;
  46. import org.slf4j.Logger;
  47. import org.slf4j.LoggerFactory;
  48. import static com.dmdirc.util.LogUtils.USER_ERROR;
  49. /**
  50. * Allows the user to modify global client preferences.
  51. */
  52. public final class SwingPreferencesDialog extends StandardDialog implements
  53. ActionListener, ListSelectionListener {
  54. private static final Logger LOG = LoggerFactory.getLogger(SwingPreferencesDialog.class);
  55. /** Serial version UID. */
  56. private static final long serialVersionUID = 9;
  57. /** Preferences tab list, used to switch option types. */
  58. private JList<PreferencesCategory> tabList;
  59. /** Main panel. */
  60. private CategoryPanel mainPanel;
  61. /** Previously selected category. */
  62. private PreferencesCategory selected;
  63. /** Preferences Manager. */
  64. private PreferencesDialogModel manager;
  65. /** Manager loading swing worker. */
  66. private final SwingWorker<PreferencesDialogModel, Void> worker;
  67. /** The provider to use for restart dialogs. */
  68. private final DialogProvider<SwingRestartDialog> restartDialogProvider;
  69. /** The provider to use to produce a category panel. */
  70. private final Provider<CategoryPanel> categoryPanelProvider;
  71. /** Icon manager to retrieve icons from. */
  72. private final IconManager iconManager;
  73. /**
  74. * Creates a new instance of SwingPreferencesDialog.
  75. *
  76. * @param parentWindow Main window to parent dialogs on.
  77. * @param iconManager Icon manager used to retrieve images
  78. * @param restartDialogProvider The provider to use for restart dialogs.
  79. * @param dialogModelProvider The provider to use to get a dialog model.
  80. * @param categoryPanelProvider The provider to use to produce a category panel.
  81. */
  82. @Inject
  83. public SwingPreferencesDialog(
  84. @MainWindow final Window parentWindow,
  85. final IconManager iconManager,
  86. @ForSettings final DialogProvider<SwingRestartDialog> restartDialogProvider,
  87. final Provider<PreferencesDialogModel> dialogModelProvider,
  88. final Provider<CategoryPanel> categoryPanelProvider) {
  89. super(parentWindow, ModalityType.MODELESS);
  90. this.iconManager = iconManager;
  91. this.restartDialogProvider = restartDialogProvider;
  92. this.categoryPanelProvider = categoryPanelProvider;
  93. initComponents();
  94. worker = new SupplierLoggingSwingWorker<>(
  95. () -> getPrefsModel(dialogModelProvider),
  96. value -> {
  97. if (value != null) {
  98. setPrefsManager(value);
  99. }
  100. });
  101. worker.execute();
  102. }
  103. private PreferencesDialogModel getPrefsModel(
  104. final Provider<PreferencesDialogModel> dialogModelProvider) {
  105. mainPanel.setWaiting(true);
  106. PreferencesDialogModel prefsManager = null;
  107. try {
  108. prefsManager = dialogModelProvider.get();
  109. } catch (IllegalArgumentException ex) {
  110. mainPanel.setError(ex.getMessage());
  111. LOG.error(USER_ERROR, "Unable to load the preferences dialog", ex);
  112. }
  113. return prefsManager;
  114. }
  115. private void setPrefsManager(final PreferencesDialogModel manager) {
  116. this.manager = manager;
  117. ((DefaultListModel<PreferencesCategory>) tabList.getModel()).clear();
  118. mainPanel.setCategory(null);
  119. final int count = countCategories(manager.getCategories());
  120. tabList.setCellRenderer(new PreferencesListCellRenderer(iconManager, count));
  121. addCategories(manager.getCategories());
  122. }
  123. /**
  124. * Initialises GUI components.
  125. */
  126. private void initComponents() {
  127. mainPanel = categoryPanelProvider.get();
  128. tabList = new JList<>(new DefaultListModel<>());
  129. tabList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
  130. tabList.addListSelectionListener(this);
  131. ListScroller.register(tabList);
  132. final JScrollPane tabListScrollPane = new JScrollPane(tabList);
  133. tabListScrollPane.setHorizontalScrollBarPolicy(
  134. ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
  135. setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
  136. setTitle("Preferences");
  137. setResizable(false);
  138. tabList.setBorder(BorderFactory.createEtchedBorder());
  139. orderButtons(new JButton(), new JButton());
  140. getOkButton().addActionListener(this);
  141. getCancelButton().addActionListener(this);
  142. setLayout(new MigLayout("pack, hmin min(80sp, 700), " + "hmax min(700, 80sp)"));
  143. add(tabListScrollPane, "w 150!, growy, pushy");
  144. add(mainPanel, "wrap, w 480!, pushy, growy, pushy");
  145. add(getLeftButton(), "span, split, right");
  146. add(getRightButton(), "right");
  147. }
  148. /**
  149. * Adds the categories from the preferences manager, clearing existing categories first.
  150. */
  151. private void addCategories(final Iterable<PreferencesCategory> categories) {
  152. UIUtilities.invokeLater(() -> {
  153. tabList.removeListSelectionListener(this);
  154. for (PreferencesCategory category : categories) {
  155. if (!category.isInline()) {
  156. ((DefaultListModel<PreferencesCategory>) tabList.getModel()).addElement(
  157. category);
  158. }
  159. addCategories(category.getSubcats());
  160. }
  161. tabList.addListSelectionListener(this);
  162. tabList.setSelectedIndex(0);
  163. });
  164. mainPanel.setWaiting(false);
  165. }
  166. /**
  167. * Counts the number of categories that will be displayed in the list panel.
  168. *
  169. * @param categories The collection of categories to inspect
  170. *
  171. * @return The number of those categories (including children) that will be displayed
  172. *
  173. * @since 0.6.3m1rc3
  174. */
  175. private int countCategories(final Iterable<PreferencesCategory> categories) {
  176. int count = 0;
  177. for (PreferencesCategory cat : categories) {
  178. if (!cat.isInline()) {
  179. count += 1 + countCategories(cat.getSubcats());
  180. }
  181. }
  182. return count;
  183. }
  184. @Override
  185. public void actionPerformed(final ActionEvent actionEvent) {
  186. if (selected != null) {
  187. selected.fireCategoryDeselected();
  188. selected = null;
  189. }
  190. mainPanel.setCategory(null);
  191. if (actionEvent != null && getOkButton().equals(actionEvent.getSource())) {
  192. saveOptions();
  193. }
  194. UIUtilities.invokeOffEDT(() -> {
  195. if (manager != null) {
  196. manager.dismiss();
  197. }
  198. });
  199. dispose();
  200. }
  201. @Override
  202. public void valueChanged(final ListSelectionEvent e) {
  203. if (!e.getValueIsAdjusting()) {
  204. PreferencesCategory node = null;
  205. try {
  206. node = tabList.getSelectedValue();
  207. } catch (ArrayIndexOutOfBoundsException ex) {
  208. //I hate the JVM
  209. }
  210. if (node == null) {
  211. tabList.setSelectedValue(selected, true);
  212. return;
  213. }
  214. if (node == selected) {
  215. return;
  216. }
  217. if (selected != null) {
  218. selected.fireCategoryDeselected();
  219. }
  220. final int index = tabList.getSelectedIndex();
  221. tabList.scrollRectToVisible(tabList.getCellBounds(index, index));
  222. selected = node;
  223. selected.fireCategorySelected();
  224. mainPanel.setCategory(selected);
  225. }
  226. }
  227. public void saveOptions() {
  228. if (manager != null && manager.save()) {
  229. dispose();
  230. restartDialogProvider.displayOrRequestFocus();
  231. }
  232. }
  233. @Override
  234. public void dispose() {
  235. synchronized (this) {
  236. if (worker != null && !worker.isDone()) {
  237. worker.cancel(true);
  238. }
  239. if (manager != null) {
  240. manager.close();
  241. }
  242. super.dispose();
  243. }
  244. }
  245. }