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 11KB

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