source: trunk/gli/src/org/greenstone/gatherer/Dictionary.java@ 5571

Last change on this file since 5571 was 5571, checked in by mdewsnip, 21 years ago

More small updates and tooltips added.

  • Property svn:keywords set to Author Date Id Revision
File size: 17.7 KB
Line 
1/**
2 *#########################################################################
3 *
4 * A component of the Gatherer application, part of the Greenstone digital
5 * library suite from the New Zealand Digital Library Project at the
6 * University of Waikato, New Zealand.
7 *
8 * <BR><BR>
9 *
10 * Author: John Thompson, Greenstone Digital Library, University of Waikato
11 *
12 * <BR><BR>
13 *
14 * Copyright (C) 1999 New Zealand Digital Library Project
15 *
16 * <BR><BR>
17 *
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
22 *
23 * <BR><BR>
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * <BR><BR>
31 *
32 * You should have received a copy of the GNU General Public License
33 * along with this program; if not, write to the Free Software
34 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
35 *########################################################################
36 */
37package org.greenstone.gatherer;
38
39import java.awt.Component;
40import java.io.*;
41import java.util.*;
42import javax.swing.*;
43import javax.swing.plaf.*;
44import javax.swing.text.*;
45import javax.swing.tree.*;
46import org.greenstone.gatherer.util.Utility;
47
48/** Extends the ResourceBundle class to allow for the automatic insertion of arguments. Note that the key names beginning Farg are reserved for formatting. <BR>
49 * <BR>
50 * Property files usable by this class have the Backus-Naur form: <BR>
51 * <BR>
52 * FileLine ::= Comment | Mapping <BR>
53 * Comment ::= '#' SString <BR>
54 * Mapping ::= NZString ':' SString ( Argument SString )* <BR>
55 * NZString ::= ( Char | Int ) SString <BR>
56 * Argument ::= '{' Int '}' <BR>
57 * SString ::= String . ['"','#',...] -> ['\"','\#',...] <BR>
58 * <BR>
59 * In order to add a new dictionary Locale, simply copy the existing dictionary.properties files, replace the values (Strings after the ':') with the new language specific ones being careful to maintain formatting and Gatherer placed arguments, then save the new dictionary as: <br>
60 * <BR>
61 * dictionary_<I>locale</I>.properties<BR>
62 * <BR>
63 * where locale is made of two two-letter codes seperated by an underscore. The first code is in lower-case and defines the language. The second is in upper-case and defines the country. For example the default dictionary could also correctly be called:
64 * <BR>
65 * dictionary_en_NZ.properties<BR>
66 * @author John Thompson, Greenstone Digital Library, University of Waikato
67 * @version 2.3
68 */
69public class Dictionary
70 extends HashMap {
71
72 static final private boolean KEY_LIST_DEBUG = false;
73 static final private String KEY_LIST_FILENAME = "keylist.txt";
74
75 /** A String which more explicitly states the Locale of this dictionary. */
76 public String language = null;
77 /** A static reference to ourself. */
78 static public Dictionary self;
79 /** The font used when displaying various html text. */
80 private FontUIResource font = null;
81 /** A reference to remind us of the current locale. */
82 private Locale locale = null;
83 /** The ResourceBundle which contains the raw key-value mappings. Loaded from a file named "dictionary<I>locale</I>.properties*/
84 private ResourceBundle dictionary = null;
85
86 private TreeSet key_list = null;;
87
88 /** Constructs the Dictionary class by first checking if a Locale has been set. If not the default locale is used, and a ResourceBundle is created. Finally a single important String, Language, is made available outside the class so a more read-able version of the Locale of this Dictionary is present.
89 * @param locale The <strong>Locale</strong> used to load the desired dictionary resource bundle.
90 */
91 public Dictionary(Locale locale, FontUIResource font) {
92 super();
93 this.self = this;
94
95 if (KEY_LIST_DEBUG) {
96 // Reload the keylist
97 File file = new File(KEY_LIST_FILENAME);
98 key_list = new TreeSet();
99 try {
100 BufferedReader br = new BufferedReader(new FileReader(file));
101 String line;
102 while((line = br.readLine()) != null) {
103 key_list.add(line);
104 }
105 br.close();
106 br = null;
107 }
108 catch(Exception error) {
109 error.printStackTrace();
110 }
111 }
112
113 // Initialize.
114 this.font = font;
115 if (locale == null) {
116 this.locale = Locale.getDefault();
117 }
118 else {
119 this.locale = locale;
120 Locale.setDefault(locale);
121 }
122 dictionary = ResourceBundle.getBundle(Utility.DICTIONARY, this.locale);
123 // Now quickly read in language name.
124 language = dictionary.getString("Language");
125 }
126
127
128 /** Change the currently loaded dictionary and update registered components */
129 public void changeDictionary(Locale locale)
130 {
131 this.locale = locale;
132
133 // Load new dictionary
134 dictionary = ResourceBundle.getBundle(Utility.DICTIONARY, locale);
135 language = dictionary.getString("Language");
136 Gatherer.println("Loaded new dictionary: " + language);
137
138 // Refresh all registered component
139 // !! TO FINISH !!
140 Gatherer.println("Updating components...");
141// ArrayList temp_list = new ArrayList();
142// Iterator components = keySet().iterator();
143// while (components.hasNext()) {
144// Component component = (Component) components.next();
145// temp_list.add(component);
146// }
147
148// for (int i = 0; i < temp_list.size(); i++) {
149// Component component = (Component) temp_list.get(i);
150// ComponentDetails details = (ComponentDetails) get(component);
151// oldSetBoth(component, details.text_key, details.text_args, details.tooltip_key);
152// }
153
154// for (Iterator keys = keySet().iterator(); keys.hasNext(); ) {
155// Component component = (Component) keys.next();
156// ComponentDetails details = (ComponentDetails) get(component);
157// oldSetBoth(component, details.text_key, details.text_args, details.tooltip_key);
158// }
159 }
160
161
162 /** Change the currently loaded dictionary and update registered (ie dynamic) components as possible. */
163// public void oldChangeDictionary(Locale locale) {
164// this.locale = locale;
165// // Load new dictionary
166// dictionary = ResourceBundle.getBundle(Utility.DICTIONARY, locale);
167// language = dictionary.getString("Language");
168// Gatherer.println("Having loaded new dictionary: " + language);
169// // Refresh all registered component
170// Gatherer.println("Updating components");
171// for(Iterator keys = keySet().iterator(); keys.hasNext(); ) {
172// Object component = keys.next();
173// String[] args = (String[]) get(component);
174// if(component instanceof AbstractButton) {
175// register((AbstractButton)component, args, true);
176// }
177// else if(component instanceof JComboBox) {
178// register((JComboBox)component, args, true);
179// }
180// else if(component instanceof JDialog) {
181// register((JDialog)component, args, true);
182// }
183// else if(component instanceof JFrame) {
184// register((JFrame)component, args, true);
185// }
186// else if(component instanceof JLabel) {
187// register((JLabel)component, args, true);
188// }
189// else if(component instanceof JTabbedPane) {
190// register((JTabbedPane)component, args, true);
191// }
192// else if(component instanceof JTextComponent) {
193// register((JTextComponent)component, args, true);
194// }
195// else if(component instanceof JTree) {
196// register((JTree)component, args, true);
197// }
198// else if(component instanceof TitledBorder) {
199// register((TitledBorder)component, args, true);
200// }
201// args = null;
202// component = null;
203// }
204// }
205
206 /** Remove the component from our registered components list. */
207 public void deregister(Object component) {
208 remove(component);
209 }
210
211 public void destroy() {
212 if(key_list != null) {
213 try {
214 FileOutputStream fos = new FileOutputStream(KEY_LIST_FILENAME);
215 for(Iterator iter = key_list.iterator(); iter.hasNext(); ) {
216 String value = (String) iter.next();
217 fos.write(value.getBytes());
218 fos.write('\n');
219 value = null;
220 }
221 fos.close();
222 fos = null;
223 }
224 catch(Exception error) {
225 error.printStackTrace();
226 }
227 }
228 }
229
230
231 static public String newget(String key)
232 {
233 return self.get(key);
234 }
235
236
237 static public String newget(String key, String arg)
238 {
239 String[] args = new String[1];
240 args[0] = arg;
241 return self.get(key, args);
242 }
243
244
245 static public String newget(String key, String[] args)
246 {
247 return self.get(key, args);
248 }
249
250
251 /**
252 * @deprecated
253 * Overloaded to call get with both a key and an empty argument array.
254 * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle.
255 * @return A <strong>String</strong> which has been referenced by the key String and that either contains no argument fields, or has had the argument fields automatiically populated with formatting Strings of with argument String provided in the get call.
256 */
257 public String get(String key) {
258 return get(key, (String[])null);
259 }
260 /**
261 * @deprecated
262 * Convienence method with transforms the second string argument into a string array. */
263 public String get(String key, String arg) {
264 String[] args = new String[1];
265 args[0] = arg;
266 return get(key, args);
267 }
268
269 /** Used to retrieve a property value from the Locale specific ResourceBundle, based upon the key and arguments supplied. If the key cannot be found or if some other part of the call fails a default (English) error message is returned. <BR>
270 * Here the get recieves a second argument which is an array of Strings used to populate argument fields, denoted {<I>n</I>}, within the value String returned. Note that argument numbers greater than or equal to 32 are automatically mapped to the formatting String named Farg<I>n</I>.
271 * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle.
272 * @param args A <strong>String[]</strong> used to populate argument fields within the complete String.
273 * @return A <strong>String</strong> which has been referenced by the key String and that either contains no argument fields, or has had the argument fields automatiically populated with formatting Strings of with argument String provided in the get call.
274 */
275 public String get(String key, String args[]) {
276 if(key_list != null) {
277 synchronized(this) {
278 key_list.add(key);
279 }
280 }
281 try {
282 String initial = dictionary.getString(key);
283 // If the string contains arguments we have to insert them.
284 String complete = "";
285 // While we still have initial string left.
286 while(initial.length() > 0 && initial.indexOf('{') != -1 && initial.indexOf('}') != -1) {
287 // Remove preamble
288 int opening = initial.indexOf('{');
289 int closing = initial.indexOf('}');
290 complete = complete + initial.substring(0, opening);
291 // Parse arg_num
292 String arg_str = initial.substring(opening + 1, closing);
293 int arg_num = Integer.parseInt(arg_str);
294 if(closing + 1 < initial.length()) {
295 initial = initial.substring(closing + 1);
296 }
297 else {
298 initial = "";
299 }
300 // Insert argument
301 if(args != null && 0 <= arg_num && arg_num < args.length) {
302 complete = complete + args[arg_num];
303 }
304 else if(arg_num >= 32) {
305 String f_subargs[] = new String[1];
306 if(font != null) {
307 f_subargs[0] = font.getFontName();
308 }
309 else {
310 f_subargs[0] = "Arial";
311 }
312 complete = complete + get("Farg" + arg_num, f_subargs);
313 }
314 }
315 return complete + initial;
316 }
317 catch (Exception e) {
318 System.err.println("Missing value for key: " + key);
319 return key;
320 }
321 }
322
323 /** Retrieve the two letter code of the current language we are using, according to the stored locale.
324 * @return A <strong>String</strong> containing the two letter ISO639 language code.
325 */
326 public String getLanguage() {
327 return locale.getLanguage();
328 }
329
330
331 static public void setBoth(Component component, String text_key, String tooltip_key)
332 {
333 setText(component, text_key);
334 setTooltip(component, tooltip_key);
335 }
336
337
338 static public void setText(Component component, String text_key)
339 {
340 setText(component, text_key, null);
341 }
342
343
344 static public void setText(Component component, String text_key, String[] args)
345 {
346 if (component != null) {
347 // Update the component using the AWTEvent queue
348 ComponentDetails details = new ComponentDetails(true, text_key, args, false, null);
349 ComponentUpdateTask task = new ComponentUpdateTask(component, details);
350 SwingUtilities.invokeLater(task);
351 }
352 }
353
354
355 static public void setTooltip(Component component, String tooltip_key)
356 {
357 if (component != null) {
358 // Update the component using the AWTEvent queue
359 ComponentDetails details = new ComponentDetails(false, null, null, true, tooltip_key);
360 ComponentUpdateTask task = new ComponentUpdateTask(component, details);
361 SwingUtilities.invokeLater(task);
362 }
363 }
364
365
366 static public void registerBoth(Component component, String text_key, String tooltip_key)
367 {
368 registerText(component, text_key, null);
369 registerTooltip(component, tooltip_key);
370 }
371
372
373 static public void registerText(Component component, String text_key)
374 {
375 registerText(component, text_key, null);
376 }
377
378
379 static public void registerText(Component component, String text_key, String[] args)
380 {
381 if (component != null) {
382 // Update the component's details
383 ComponentDetails details = (ComponentDetails) self.remove(component);
384 if (details == null) {
385 details = new ComponentDetails(true, text_key, args, false, null);
386 }
387 else {
388 details.has_text = true;
389 details.text_key = text_key;
390 details.text_args = args;
391 }
392 self.put(component, details);
393
394 // Update the component using the AWTEvent queue
395 ComponentUpdateTask task = new ComponentUpdateTask(component, details);
396 SwingUtilities.invokeLater(task);
397 }
398 }
399
400
401 static public void registerTooltip(Component component, String tooltip_key)
402 {
403 if (component != null) {
404 // Update the component's details
405 ComponentDetails details = (ComponentDetails) self.remove(component);
406 if (details == null) {
407 details = new ComponentDetails(false, null, null, true, tooltip_key);
408 }
409 else {
410 details.has_tooltip = true;
411 details.tooltip_key = tooltip_key;
412 }
413 self.put(component, details);
414
415 // Update the component using the AWTEvent queue
416 ComponentUpdateTask task = new ComponentUpdateTask(component, details);
417 SwingUtilities.invokeLater(task);
418 }
419 }
420
421
422 static private class ComponentDetails
423 {
424 public boolean has_text;
425 public String text_key;
426 public String[] text_args;
427 public boolean has_tooltip;
428 public String tooltip_key;
429
430 public ComponentDetails(boolean has_text, String text_key, String[] text_args,
431 boolean has_tooltip, String tooltip_key)
432 {
433 this.has_text = has_text;
434 this.text_key = text_key;
435 this.text_args = text_args;
436 this.has_tooltip = has_tooltip;
437 this.tooltip_key = tooltip_key;
438 }
439 }
440
441
442 static private class ComponentUpdateTask
443 implements Runnable {
444
445 private Component component;
446 private String text = null;
447 private String tooltip = null;
448
449 public ComponentUpdateTask(Component component, ComponentDetails details)
450 {
451 this.component = component;
452
453 if (details.has_text) {
454 text = self.get(details.text_key, details.text_args);
455 }
456 if (details.has_tooltip) {
457 tooltip = self.get(details.tooltip_key, (String[]) null);
458 }
459 }
460
461
462 public void run()
463 {
464 // If the component has text
465 if (text != null) {
466 if (component instanceof AbstractButton) {
467 ((AbstractButton) component).setText(text);
468 }
469 else if (component instanceof JDialog) {
470 ((JDialog) component).setTitle(text);
471 }
472 else if (component instanceof JLabel) {
473 ((JLabel) component).setText(text);
474 }
475 else if (component instanceof JProgressBar) {
476 ((JProgressBar) component).setString(text);
477 }
478 else if (component instanceof JTextComponent) {
479 ((JTextComponent) component).setText(text);
480 ((JTextComponent) component).setCaretPosition(0);
481 }
482 else {
483 System.err.println("Unhandled component: " + component.getClass());
484 }
485 }
486
487 // If the component has a tooltip
488 if (tooltip != null) {
489 if (component instanceof JComponent) {
490 ((JComponent) component).setToolTipText(tooltip);
491 }
492 }
493 }
494 }
495
496
497 /**
498 * @deprecated
499 * Register a tab pane component. */
500 static public void register(JTabbedPane component)
501 {
502 if(component != null) {
503 String[] args = new String[component.getTabCount()];
504
505 // Iterate through the tabbed panes tabs, updating values and recording the original key of each item in args.
506 for(int i = 0; i < args.length; i++) {
507 if (args[i] == null) {
508 args[i] = component.getTitleAt(i);
509 }
510 String value = self.get(args[i], (String[])null);
511 String tooltip = self.get(args[i] + "_Tooltip", (String[])null);
512 JTabbedPaneChangeTask task = new JTabbedPaneChangeTask(component, args[i], i, value, tooltip);
513 SwingUtilities.invokeLater(task);
514 }
515 }
516 }
517
518
519 /** Updates a tabbed panes tab title and tooltip. */
520 static private class JTabbedPaneChangeTask
521 implements Runnable {
522 protected String key;
523 protected String value;
524 private int index;
525 private JTabbedPane component;
526 private String tooltip;
527
528 public JTabbedPaneChangeTask(JTabbedPane component, String key, int index, String value, String tooltip) {
529 this.key = key;
530 this.value = value;
531 this.component = component;
532 this.index = index;
533 this.tooltip = tooltip;
534 }
535 public void run() {
536 component.setTitleAt(index, value);
537 if(!tooltip.equals(key+"_Tooltip")) {
538 component.setToolTipTextAt(index, tooltip);
539 }
540 else {
541 component.setToolTipTextAt(index, null);
542 }
543 }
544 }
545}
Note: See TracBrowser for help on using the repository browser.