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

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

Added a title to the help frame.

  • 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.*;
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 get(String key)
232 {
233 return self.oldget(key);
234 }
235
236
237 static public String get(String key, String arg)
238 {
239 String[] args = new String[1];
240 args[0] = arg;
241 return self.oldget(key, args);
242 }
243
244
245 static public String get(String key, String[] args)
246 {
247 return self.oldget(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 oldget(String key) {
258 return oldget(key, (String[])null);
259 }
260
261 /**
262 * @deprecated
263 * Convienence method with transforms the second string argument into a string array. */
264 public String oldget(String key, String arg) {
265 String[] args = new String[1];
266 args[0] = arg;
267 return oldget(key, args);
268 }
269
270 /** 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>
271 * 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>.
272 * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle.
273 * @param args A <strong>String[]</strong> used to populate argument fields within the complete String.
274 * @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.
275 */
276 public String oldget(String key, String args[]) {
277 if(key_list != null) {
278 synchronized(this) {
279 key_list.add(key);
280 }
281 }
282 try {
283 String initial = dictionary.getString(key);
284 // If the string contains arguments we have to insert them.
285 String complete = "";
286 // While we still have initial string left.
287 while(initial.length() > 0 && initial.indexOf('{') != -1 && initial.indexOf('}') != -1) {
288 // Remove preamble
289 int opening = initial.indexOf('{');
290 int closing = initial.indexOf('}');
291 complete = complete + initial.substring(0, opening);
292 // Parse arg_num
293 String arg_str = initial.substring(opening + 1, closing);
294 int arg_num = Integer.parseInt(arg_str);
295 if(closing + 1 < initial.length()) {
296 initial = initial.substring(closing + 1);
297 }
298 else {
299 initial = "";
300 }
301 // Insert argument
302 if(args != null && 0 <= arg_num && arg_num < args.length) {
303 complete = complete + args[arg_num];
304 }
305 else if(arg_num >= 32) {
306 String f_subargs[] = new String[1];
307 if(font != null) {
308 f_subargs[0] = font.getFontName();
309 }
310 else {
311 f_subargs[0] = "Arial";
312 }
313 complete = complete + oldget("Farg" + arg_num, f_subargs);
314 }
315 }
316 return complete + initial;
317 }
318 catch (Exception e) {
319 System.err.println("Missing value for key: " + key);
320 return key;
321 }
322 }
323
324 /** Retrieve the two letter code of the current language we are using, according to the stored locale.
325 * @return A <strong>String</strong> containing the two letter ISO639 language code.
326 */
327 public String getLanguage() {
328 return locale.getLanguage();
329 }
330
331
332 static public void setBoth(Component component, String text_key, String tooltip_key)
333 {
334 setText(component, text_key);
335 setTooltip(component, tooltip_key);
336 }
337
338
339 static public void setText(Component component, String text_key)
340 {
341 setText(component, text_key, null);
342 }
343
344
345 static public void setText(Component component, String text_key, String[] args)
346 {
347 if (component != null) {
348 // Update the component using the AWTEvent queue
349 ComponentDetails details = new ComponentDetails(true, text_key, args, false, null);
350 ComponentUpdateTask task = new ComponentUpdateTask(component, details);
351 SwingUtilities.invokeLater(task);
352 }
353 }
354
355
356 static public void setTooltip(Component component, String tooltip_key)
357 {
358 if (component != null) {
359 // Update the component using the AWTEvent queue
360 ComponentDetails details = new ComponentDetails(false, null, null, true, tooltip_key);
361 ComponentUpdateTask task = new ComponentUpdateTask(component, details);
362 SwingUtilities.invokeLater(task);
363 }
364 }
365
366
367 static public void registerBoth(Component component, String text_key, String tooltip_key)
368 {
369 registerText(component, text_key, null);
370 registerTooltip(component, tooltip_key);
371 }
372
373
374 static public void registerText(Component component, String text_key)
375 {
376 registerText(component, text_key, null);
377 }
378
379
380 static public void registerText(Component component, String text_key, String[] args)
381 {
382 if (component != null) {
383 // Update the component's details
384 ComponentDetails details = (ComponentDetails) self.remove(component);
385 if (details == null) {
386 details = new ComponentDetails(true, text_key, args, false, null);
387 }
388 else {
389 details.has_text = true;
390 details.text_key = text_key;
391 details.text_args = args;
392 }
393 self.put(component, details);
394
395 // Update the component using the AWTEvent queue
396 ComponentUpdateTask task = new ComponentUpdateTask(component, details);
397 SwingUtilities.invokeLater(task);
398 }
399 }
400
401
402 static public void registerTooltip(Component component, String tooltip_key)
403 {
404 if (component != null) {
405 // Update the component's details
406 ComponentDetails details = (ComponentDetails) self.remove(component);
407 if (details == null) {
408 details = new ComponentDetails(false, null, null, true, tooltip_key);
409 }
410 else {
411 details.has_tooltip = true;
412 details.tooltip_key = tooltip_key;
413 }
414 self.put(component, details);
415
416 // Update the component using the AWTEvent queue
417 ComponentUpdateTask task = new ComponentUpdateTask(component, details);
418 SwingUtilities.invokeLater(task);
419 }
420 }
421
422
423 static private class ComponentDetails
424 {
425 public boolean has_text;
426 public String text_key;
427 public String[] text_args;
428 public boolean has_tooltip;
429 public String tooltip_key;
430
431 public ComponentDetails(boolean has_text, String text_key, String[] text_args,
432 boolean has_tooltip, String tooltip_key)
433 {
434 this.has_text = has_text;
435 this.text_key = text_key;
436 this.text_args = text_args;
437 this.has_tooltip = has_tooltip;
438 this.tooltip_key = tooltip_key;
439 }
440 }
441
442
443 static private class ComponentUpdateTask
444 implements Runnable {
445
446 private Component component;
447 private String text = null;
448 private String tooltip = null;
449
450 public ComponentUpdateTask(Component component, ComponentDetails details)
451 {
452 this.component = component;
453
454 if (details.has_text) {
455 text = self.oldget(details.text_key, details.text_args);
456 }
457 if (details.has_tooltip) {
458 tooltip = self.oldget(details.tooltip_key, (String[]) null);
459 }
460 }
461
462
463 public void run()
464 {
465 // If the component has text
466 if (text != null) {
467 if (component instanceof AbstractButton) {
468 ((AbstractButton) component).setText(text);
469 }
470 else if (component instanceof Frame) {
471 ((Frame) component).setTitle(text);
472 }
473 else if (component instanceof JDialog) {
474 ((JDialog) component).setTitle(text);
475 }
476 else if (component instanceof JLabel) {
477 ((JLabel) component).setText(text);
478 }
479 else if (component instanceof JProgressBar) {
480 ((JProgressBar) component).setString(text);
481 }
482 else if (component instanceof JTextComponent) {
483 ((JTextComponent) component).setText(text);
484 ((JTextComponent) component).setCaretPosition(0);
485 }
486 else {
487 System.err.println("Unhandled component: " + component.getClass());
488 }
489 }
490
491 // If the component has a tooltip
492 if (tooltip != null) {
493 if (component instanceof JComponent) {
494 ((JComponent) component).setToolTipText(tooltip);
495 }
496 }
497 }
498 }
499
500
501 /**
502 * Register a tab pane component. This will be deprecated eventually. */
503 static public void register(JTabbedPane component)
504 {
505 if (component != null) {
506 String[] args = new String[component.getTabCount()];
507
508 // Iterate through the tabbed panes tabs, updating values and recording the original key of each item in args.
509 for (int i = 0; i < args.length; i++) {
510 if (args[i] == null) {
511 args[i] = component.getTitleAt(i);
512 }
513 String value = self.oldget(args[i], (String[]) null);
514 String tooltip = self.oldget(args[i] + "_Tooltip", (String[]) null);
515 JTabbedPaneChangeTask task = new JTabbedPaneChangeTask(component, i, value, tooltip);
516 SwingUtilities.invokeLater(task);
517 }
518 }
519 }
520
521
522 /** Updates a tabbed panes tab title and tooltip. */
523 static private class JTabbedPaneChangeTask
524 implements Runnable {
525
526 private JTabbedPane component;
527 private int index;
528 private String value;
529 private String tooltip;
530
531 public JTabbedPaneChangeTask(JTabbedPane component, int index, String value, String tooltip) {
532 this.component = component;
533 this.index = index;
534 this.value = value;
535 this.tooltip = tooltip;
536 }
537
538 public void run() {
539 component.setTitleAt(index, value);
540 component.setToolTipTextAt(index, tooltip);
541 }
542 }
543}
Note: See TracBrowser for help on using the repository browser.