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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. /*
  2. * Copyright (c) 2006-2015 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.prefs;
  23. import com.dmdirc.addons.ui_swing.UIUtilities;
  24. import com.dmdirc.addons.ui_swing.components.IconManager;
  25. import com.dmdirc.addons.ui_swing.components.ListScroller;
  26. import com.dmdirc.addons.ui_swing.components.SupplierLoggingSwingWorker;
  27. import com.dmdirc.addons.ui_swing.dialogs.StandardDialog;
  28. import com.dmdirc.addons.ui_swing.dialogs.updater.SwingRestartDialog;
  29. import com.dmdirc.addons.ui_swing.injection.DialogModule.ForSettings;
  30. import com.dmdirc.addons.ui_swing.injection.DialogProvider;
  31. import com.dmdirc.addons.ui_swing.injection.MainWindow;
  32. import com.dmdirc.config.prefs.PreferencesCategory;
  33. import com.dmdirc.config.prefs.PreferencesDialogModel;
  34. import java.awt.Window;
  35. import java.awt.event.ActionEvent;
  36. import java.awt.event.ActionListener;
  37. import javax.inject.Inject;
  38. import javax.inject.Provider;
  39. import javax.swing.BorderFactory;
  40. import javax.swing.DefaultListModel;
  41. import javax.swing.JButton;
  42. import javax.swing.JList;
  43. import javax.swing.JScrollPane;
  44. import javax.swing.ListSelectionModel;
  45. import javax.swing.ScrollPaneConstants;
  46. import javax.swing.SwingWorker;
  47. import javax.swing.WindowConstants;
  48. import javax.swing.event.ListSelectionEvent;
  49. import javax.swing.event.ListSelectionListener;
  50. import net.miginfocom.swing.MigLayout;
  51. import org.slf4j.Logger;
  52. import org.slf4j.LoggerFactory;
  53. import static com.dmdirc.util.LogUtils.USER_ERROR;
  54. /**
  55. * Allows the user to modify global client preferences.
  56. */
  57. public final class SwingPreferencesDialog extends StandardDialog implements
  58. ActionListener, ListSelectionListener {
  59. private static final Logger LOG = LoggerFactory.getLogger(SwingPreferencesDialog.class);
  60. /** Serial version UID. */
  61. private static final long serialVersionUID = 9;
  62. /** Preferences tab list, used to switch option types. */
  63. private JList<PreferencesCategory> tabList;
  64. /** Main panel. */
  65. private CategoryPanel mainPanel;
  66. /** Previously selected category. */
  67. private PreferencesCategory selected;
  68. /** Preferences Manager. */
  69. private PreferencesDialogModel manager;
  70. /** Manager loading swing worker. */
  71. private final SwingWorker<PreferencesDialogModel, Void> worker;
  72. /** The provider to use for restart dialogs. */
  73. private final DialogProvider<SwingRestartDialog> restartDialogProvider;
  74. /** The provider to use to produce a category panel. */
  75. private final Provider<CategoryPanel> categoryPanelProvider;
  76. /** Icon manager to retrieve icons from. */
  77. private final IconManager iconManager;
  78. /**
  79. * Creates a new instance of SwingPreferencesDialog.
  80. *
  81. * @param parentWindow Main window to parent dialogs on.
  82. * @param iconManager Icon manager used to retrieve images
  83. * @param restartDialogProvider The provider to use for restart dialogs.
  84. * @param dialogModelProvider The provider to use to get a dialog model.
  85. * @param categoryPanelProvider The provider to use to produce a category panel.
  86. */
  87. @Inject
  88. public SwingPreferencesDialog(
  89. @MainWindow final Window parentWindow,
  90. final IconManager iconManager,
  91. @ForSettings final DialogProvider<SwingRestartDialog> restartDialogProvider,
  92. final Provider<PreferencesDialogModel> dialogModelProvider,
  93. final Provider<CategoryPanel> categoryPanelProvider) {
  94. super(parentWindow, ModalityType.MODELESS);
  95. this.iconManager = iconManager;
  96. this.restartDialogProvider = restartDialogProvider;
  97. this.categoryPanelProvider = categoryPanelProvider;
  98. initComponents();
  99. worker = new SupplierLoggingSwingWorker<>(
  100. () -> getPrefsModel(dialogModelProvider),
  101. value -> {
  102. if (value != null) {
  103. setPrefsManager(value);
  104. }
  105. });
  106. worker.execute();
  107. }
  108. private PreferencesDialogModel getPrefsModel(
  109. final Provider<PreferencesDialogModel> dialogModelProvider) {
  110. mainPanel.setWaiting(true);
  111. PreferencesDialogModel prefsManager = null;
  112. try {
  113. prefsManager = dialogModelProvider.get();
  114. } catch (IllegalArgumentException ex) {
  115. mainPanel.setError(ex.getMessage());
  116. LOG.error(USER_ERROR, "Unable to load the preferences dialog", ex);
  117. }
  118. return prefsManager;
  119. }
  120. private void setPrefsManager(final PreferencesDialogModel manager) {
  121. this.manager = manager;
  122. ((DefaultListModel<PreferencesCategory>) tabList.getModel()).clear();
  123. mainPanel.setCategory(null);
  124. final int count = countCategories(manager.getCategories());
  125. tabList.setCellRenderer(new PreferencesListCellRenderer(iconManager, count));
  126. addCategories(manager.getCategories());
  127. }
  128. /**
  129. * Initialises GUI components.
  130. */
  131. private void initComponents() {
  132. mainPanel = categoryPanelProvider.get();
  133. tabList = new JList<>(new DefaultListModel<>());
  134. tabList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
  135. tabList.addListSelectionListener(this);
  136. ListScroller.register(tabList);
  137. final JScrollPane tabListScrollPane = new JScrollPane(tabList);
  138. tabListScrollPane.setHorizontalScrollBarPolicy(
  139. ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
  140. setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
  141. setTitle("Preferences");
  142. setResizable(false);
  143. tabList.setBorder(BorderFactory.createEtchedBorder());
  144. orderButtons(new JButton(), new JButton());
  145. getOkButton().addActionListener(this);
  146. getCancelButton().addActionListener(this);
  147. setLayout(new MigLayout("pack, hmin min(80sp, 700), " + "hmax min(700, 80sp)"));
  148. add(tabListScrollPane, "w 150!, growy, pushy");
  149. add(mainPanel, "wrap, w 480!, pushy, growy, pushy");
  150. add(getLeftButton(), "span, split, right");
  151. add(getRightButton(), "right");
  152. }
  153. /**
  154. * Adds the categories from the preferences manager, clearing existing categories first.
  155. */
  156. private void addCategories(final Iterable<PreferencesCategory> categories) {
  157. UIUtilities.invokeLater(() -> {
  158. tabList.removeListSelectionListener(this);
  159. for (PreferencesCategory category : categories) {
  160. if (!category.isInline()) {
  161. ((DefaultListModel<PreferencesCategory>) tabList.getModel()).addElement(
  162. category);
  163. }
  164. addCategories(category.getSubcats());
  165. }
  166. tabList.addListSelectionListener(this);
  167. tabList.setSelectedIndex(0);
  168. });
  169. mainPanel.setWaiting(false);
  170. }
  171. /**
  172. * Counts the number of categories that will be displayed in the list panel.
  173. *
  174. * @param categories The collection of categories to inspect
  175. *
  176. * @return The number of those categories (including children) that will be displayed
  177. *
  178. * @since 0.6.3m1rc3
  179. */
  180. private int countCategories(final Iterable<PreferencesCategory> categories) {
  181. int count = 0;
  182. for (PreferencesCategory cat : categories) {
  183. if (!cat.isInline()) {
  184. count += 1 + countCategories(cat.getSubcats());
  185. }
  186. }
  187. return count;
  188. }
  189. @Override
  190. public void actionPerformed(final ActionEvent actionEvent) {
  191. if (selected != null) {
  192. selected.fireCategoryDeselected();
  193. selected = null;
  194. }
  195. mainPanel.setCategory(null);
  196. if (actionEvent != null && getOkButton().equals(actionEvent.getSource())) {
  197. saveOptions();
  198. }
  199. UIUtilities.invokeOffEDT(() -> {
  200. if (manager != null) {
  201. manager.dismiss();
  202. }
  203. });
  204. dispose();
  205. }
  206. @Override
  207. public void valueChanged(final ListSelectionEvent e) {
  208. if (!e.getValueIsAdjusting()) {
  209. PreferencesCategory node = null;
  210. try {
  211. node = tabList.getSelectedValue();
  212. } catch (ArrayIndexOutOfBoundsException ex) {
  213. //I hate the JVM
  214. }
  215. if (node == null) {
  216. tabList.setSelectedValue(selected, true);
  217. return;
  218. }
  219. if (node == selected) {
  220. return;
  221. }
  222. if (selected != null) {
  223. selected.fireCategoryDeselected();
  224. }
  225. final int index = tabList.getSelectedIndex();
  226. tabList.scrollRectToVisible(tabList.getCellBounds(index, index));
  227. selected = node;
  228. selected.fireCategorySelected();
  229. mainPanel.setCategory(selected);
  230. }
  231. }
  232. public void saveOptions() {
  233. if (manager != null && manager.save()) {
  234. dispose();
  235. restartDialogProvider.displayOrRequestFocus();
  236. }
  237. }
  238. @Override
  239. public void dispose() {
  240. synchronized (this) {
  241. if (worker != null && !worker.isDone()) {
  242. worker.cancel(true);
  243. }
  244. if (manager != null) {
  245. manager.close();
  246. }
  247. super.dispose();
  248. }
  249. }
  250. }