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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. /*
  2. * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
  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.MainFrame;
  24. import com.dmdirc.config.prefs.PreferencesCategory;
  25. import com.dmdirc.config.prefs.PreferencesManager;
  26. import com.dmdirc.addons.ui_swing.UIUtilities;
  27. import com.dmdirc.addons.ui_swing.components.ListScroller;
  28. import com.dmdirc.addons.ui_swing.components.LoggingSwingWorker;
  29. import com.dmdirc.addons.ui_swing.dialogs.StandardDialog;
  30. import com.dmdirc.addons.ui_swing.dialogs.updater.SwingRestartDialog;
  31. import com.dmdirc.config.IdentityManager;
  32. import com.dmdirc.logger.ErrorLevel;
  33. import com.dmdirc.logger.Logger;
  34. import java.awt.event.ActionEvent;
  35. import java.awt.event.ActionListener;
  36. import java.util.Collection;
  37. import java.util.List;
  38. import java.util.concurrent.ExecutionException;
  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.WindowConstants;
  46. import javax.swing.event.ListSelectionEvent;
  47. import javax.swing.event.ListSelectionListener;
  48. import net.miginfocom.swing.MigLayout;
  49. /**
  50. * Allows the user to modify global client preferences.
  51. */
  52. public final class SwingPreferencesDialog extends StandardDialog implements
  53. ActionListener, ListSelectionListener {
  54. /**
  55. * A version number for this class. It should be changed whenever the
  56. * class structure is changed (or anything else that would prevent
  57. * serialized objects being unserialized with the new class).
  58. */
  59. private static final long serialVersionUID = 9;
  60. /** Previously instantiated instance of SwingPreferencesDialog. */
  61. private static volatile SwingPreferencesDialog me;
  62. /** Preferences tab list, used to switch option types. */
  63. private JList tabList;
  64. /** Main panel. */
  65. private CategoryPanel mainPanel;
  66. /** Previously selected category. */
  67. private PreferencesCategory selected;
  68. /** Preferences Manager. */
  69. private PreferencesManager manager;
  70. /** Manager loading swing worker. */
  71. private LoggingSwingWorker worker;
  72. /** Parent window. */
  73. private MainFrame parentWindow;
  74. /** Panel size. */
  75. protected int panelSize = 0;
  76. /**
  77. * Creates a new instance of SwingPreferencesDialog.
  78. *
  79. * @param parentWindow Parent window
  80. */
  81. private SwingPreferencesDialog(final MainFrame parentWindow) {
  82. super(parentWindow, ModalityType.MODELESS);
  83. this.parentWindow = parentWindow;
  84. initComponents();
  85. worker = new LoggingSwingWorker<PreferencesManager, Void>() {
  86. /** {@inheritDoc} */
  87. @Override
  88. protected PreferencesManager doInBackground() throws Exception {
  89. mainPanel.setWaiting(true);
  90. return new PreferencesManager();
  91. }
  92. /** {@inheritDoc} */
  93. @Override
  94. protected void done() {
  95. if (!isCancelled()) {
  96. try {
  97. setPrefsManager(get());
  98. } catch (InterruptedException ex) {
  99. //Ignore
  100. } catch (ExecutionException ex) {
  101. Logger.appError(ErrorLevel.MEDIUM, ex.getMessage(), ex);
  102. }
  103. }
  104. }
  105. };
  106. worker.execute();
  107. }
  108. private void setPrefsManager(final PreferencesManager manager) {
  109. this.manager = manager;
  110. ((DefaultListModel) tabList.getModel()).clear();
  111. mainPanel.setCategory(null);
  112. final int count = countCategories(manager.getCategories());
  113. tabList.setCellRenderer(new PreferencesListCellRenderer(count));
  114. addCategories(manager.getCategories());
  115. }
  116. /**
  117. * Returns the instance of SwingPreferencesDialog.
  118. *
  119. * @param parentWindow Parent window
  120. */
  121. public static void showSwingPreferencesDialog(final MainFrame parentWindow) {
  122. me = getSwingPreferencesDialog(parentWindow);
  123. me.display();
  124. }
  125. /**
  126. * Returns the current instance of the ErrorListDialog.
  127. *
  128. * @param parentWindow Parent window
  129. *
  130. * @return The current PluginDErrorListDialogialog instance
  131. */
  132. public static SwingPreferencesDialog getSwingPreferencesDialog(
  133. final MainFrame parentWindow) {
  134. synchronized (SwingPreferencesDialog.class) {
  135. if (me == null) {
  136. me = new SwingPreferencesDialog(parentWindow);
  137. }
  138. }
  139. return me;
  140. }
  141. /**
  142. * Initialises GUI components.
  143. */
  144. private void initComponents() {
  145. mainPanel = new CategoryPanel(this);
  146. tabList = new JList(new DefaultListModel());
  147. tabList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
  148. tabList.addListSelectionListener(this);
  149. new ListScroller(tabList);
  150. final JScrollPane tabListScrollPane = new JScrollPane(tabList);
  151. tabListScrollPane.setHorizontalScrollBarPolicy(
  152. JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
  153. setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
  154. setTitle("Preferences");
  155. setResizable(false);
  156. tabList.setBorder(BorderFactory.createEtchedBorder());
  157. orderButtons(new JButton(), new JButton());
  158. getOkButton().addActionListener(this);
  159. getCancelButton().addActionListener(this);
  160. final MigLayout layout = new MigLayout("pack, hmin min(80sp, 700), " +
  161. "hmax min(700, 80sp)");
  162. setLayout(layout);
  163. add(tabListScrollPane, "w 150!, growy, pushy");
  164. add(mainPanel, "wrap, w 480!, pushy, growy, pushy");
  165. add(getLeftButton(), "span, split, right");
  166. add(getRightButton(), "right");
  167. }
  168. /**
  169. * Adds the categories from the preferences manager, clearing existing
  170. * categories first.
  171. */
  172. private void addCategories(final List<PreferencesCategory> categories) {
  173. UIUtilities.invokeLater(new Runnable() {
  174. @Override
  175. public void run() {
  176. tabList.removeListSelectionListener(SwingPreferencesDialog.this);
  177. for (PreferencesCategory category : categories) {
  178. if (!category.isInline()) {
  179. ((DefaultListModel) tabList.getModel()).addElement(
  180. category);
  181. }
  182. addCategories(category.getSubcats());
  183. }
  184. tabList.addListSelectionListener(SwingPreferencesDialog.this);
  185. restoreActiveCategory();
  186. }
  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. * @return The number of those categories (including children) that will be displayed
  195. * @since 0.6.3m1rc3
  196. */
  197. protected int countCategories(
  198. final Collection<PreferencesCategory> categories) {
  199. int count = 0;
  200. for (PreferencesCategory cat : categories) {
  201. if (!cat.isInline()) {
  202. count += 1 + countCategories(cat.getSubcats());
  203. }
  204. }
  205. return count;
  206. }
  207. /**
  208. * Handles the actions for the dialog.
  209. *
  210. * @param actionEvent Action event
  211. */
  212. @Override
  213. public void actionPerformed(final ActionEvent actionEvent) {
  214. if (selected != null) {
  215. selected.fireCategoryDeselected();
  216. selected = null;
  217. }
  218. mainPanel.setCategory(null);
  219. if (actionEvent != null && getOkButton().equals(actionEvent.getSource())) {
  220. if (tabList.getSelectedIndex() > -1) {
  221. final PreferencesCategory node = (PreferencesCategory) tabList.
  222. getSelectedValue();
  223. IdentityManager.getConfigIdentity().setOption("dialogstate",
  224. "preferences", node.getPath());
  225. }
  226. saveOptions();
  227. }
  228. new LoggingSwingWorker() {
  229. @Override
  230. protected Object doInBackground() throws Exception {
  231. if (manager != null) {
  232. manager.dismiss();
  233. }
  234. return null;
  235. }
  236. }.execute();
  237. dispose();
  238. }
  239. /**
  240. * {@inheritDoc}
  241. *
  242. * @since 0.6.3m1
  243. */
  244. @Override
  245. public void valueChanged(final ListSelectionEvent e) {
  246. if (!e.getValueIsAdjusting()) {
  247. PreferencesCategory node = null;
  248. try {
  249. node = (PreferencesCategory) tabList.getSelectedValue();
  250. } catch (ArrayIndexOutOfBoundsException ex) {
  251. //I hate the JVM
  252. }
  253. if (node == null) {
  254. tabList.setSelectedValue(selected, true);
  255. return;
  256. }
  257. if (node == selected) {
  258. return;
  259. }
  260. if (selected != null) {
  261. selected.fireCategoryDeselected();
  262. }
  263. final int index = tabList.getSelectedIndex();
  264. tabList.scrollRectToVisible(tabList.getCellBounds(index, index));
  265. selected = node;
  266. if (selected != null) {
  267. selected.fireCategorySelected();
  268. }
  269. mainPanel.setCategory(selected);
  270. }
  271. }
  272. /**
  273. * Returns the selected category.
  274. *
  275. * @return Selected category
  276. */
  277. protected PreferencesCategory getSelectedCategory() {
  278. return selected;
  279. }
  280. /** {@inheritDoc} */
  281. public void saveOptions() {
  282. if (manager != null) {
  283. if (manager.save()) {
  284. dispose();
  285. new SwingRestartDialog(parentWindow,
  286. ModalityType.APPLICATION_MODAL,
  287. "apply settings").display();
  288. }
  289. }
  290. }
  291. private void restoreActiveCategory() {
  292. final String oldCategoryPath = IdentityManager.getGlobalConfig().
  293. getOption("dialogstate", "preferences");
  294. final DefaultListModel model = (DefaultListModel) tabList.getModel();
  295. int indexToSelect = 0;
  296. for (int i = 0; i < model.getSize(); i++) {
  297. final PreferencesCategory category =
  298. (PreferencesCategory) model.get(i);
  299. if (oldCategoryPath.equals(category.getPath())) {
  300. indexToSelect = i;
  301. break;
  302. }
  303. }
  304. tabList.setSelectedIndex(indexToSelect);
  305. }
  306. /**
  307. * Gets the maximum panel size.
  308. *
  309. * @return Max panel size
  310. */
  311. public int getPanelHeight() {
  312. return panelSize;
  313. }
  314. /**
  315. * Sets the panel size to the specified value.
  316. *
  317. * @param panelSize New panel size
  318. */
  319. protected void setPanelHeight(final int panelSize) {
  320. this.panelSize = panelSize;
  321. }
  322. /** {@inheritDoc} */
  323. @Override
  324. public void dispose() {
  325. synchronized (SwingPreferencesDialog.this) {
  326. if (worker != null && !worker.isDone()) {
  327. worker.cancel(true);
  328. }
  329. if (manager != null) {
  330. manager.close();
  331. }
  332. if (me == null) {
  333. return;
  334. }
  335. super.dispose();
  336. me = null;
  337. worker = null;
  338. manager = null;
  339. }
  340. }
  341. }