OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Rev 156 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
18 ilm 1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
5
 *
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
7
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
8
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
9
 * language governing permissions and limitations under the License.
10
 *
11
 * When distributing the software, include this License Header Notice in each file.
12
 */
13
 
14
 package org.openconcerto.erp.config;
15
 
16
import org.openconcerto.erp.core.common.ui.StatusPanel;
149 ilm 17
import org.openconcerto.erp.panel.UserExitConf;
18
import org.openconcerto.erp.panel.UserExitPanel;
93 ilm 19
import org.openconcerto.erp.rights.MenuComboRightEditor;
149 ilm 20
import org.openconcerto.sql.PropsConfiguration;
144 ilm 21
import org.openconcerto.sql.users.UserManager;
93 ilm 22
import org.openconcerto.sql.users.rights.UserRightsManager;
18 ilm 23
import org.openconcerto.task.TodoListPanel;
24
import org.openconcerto.task.config.ComptaBasePropsConfiguration;
20 ilm 25
import org.openconcerto.ui.AutoHideTabbedPane;
19 ilm 26
import org.openconcerto.ui.MenuUtils;
18 ilm 27
import org.openconcerto.ui.SwingThreadUtils;
67 ilm 28
import org.openconcerto.ui.group.Group;
29
import org.openconcerto.ui.group.Item;
20 ilm 30
import org.openconcerto.ui.state.WindowStateManager;
18 ilm 31
import org.openconcerto.utils.JImage;
32
import org.openconcerto.utils.OSXAdapter;
33
 
34
import java.awt.Color;
35
import java.awt.Container;
19 ilm 36
import java.awt.Dimension;
18 ilm 37
import java.awt.GridBagConstraints;
38
import java.awt.GridBagLayout;
61 ilm 39
import java.awt.Image;
18 ilm 40
import java.awt.event.ActionEvent;
41
import java.awt.event.WindowAdapter;
42
import java.awt.event.WindowEvent;
73 ilm 43
import java.beans.PropertyChangeEvent;
44
import java.beans.PropertyChangeListener;
20 ilm 45
import java.io.File;
18 ilm 46
import java.util.ArrayList;
19 ilm 47
import java.util.Arrays;
73 ilm 48
import java.util.Collections;
18 ilm 49
import java.util.List;
50
 
51
import javax.swing.AbstractAction;
19 ilm 52
import javax.swing.Action;
18 ilm 53
import javax.swing.ImageIcon;
54
import javax.swing.JFrame;
55
import javax.swing.JMenu;
56
import javax.swing.JMenuBar;
57
import javax.swing.JMenuItem;
67 ilm 58
import javax.swing.JOptionPane;
18 ilm 59
import javax.swing.JSeparator;
80 ilm 60
import javax.swing.SwingUtilities;
18 ilm 61
 
80 ilm 62
import net.jcip.annotations.GuardedBy;
63
 
18 ilm 64
public class MainFrame extends JFrame {
65
 
19 ilm 66
    // menus
73 ilm 67
    public static final String STRUCTURE_MENU = "menu.organization";
68
    public static final String PAYROLL_MENU = "menu.payroll";
69
    public static final String PAYMENT_MENU = "menu.payment";
70
    public static final String STATS_MENU = "menu.stats";
71
    public static final String DECLARATION_MENU = "menu.report";
72
    public static final String STATE_MENU = "menu.accounting";
73
    public static final String LIST_MENU = "menu.list";
74
    public static final String CREATE_MENU = "menu.create";
75
    public static final String FILE_MENU = "menu.file";
76
    public static final String HELP_MENU = "menu.help";
19 ilm 77
 
18 ilm 78
    static private final List<Runnable> runnables = new ArrayList<Runnable>();
80 ilm 79
    @GuardedBy("MainFrame")
18 ilm 80
    static private MainFrame instance = null;
81
 
80 ilm 82
    // thread safe
83
    public synchronized static MainFrame getInstance() {
18 ilm 84
        return instance;
85
    }
86
 
80 ilm 87
    public synchronized static MainFrame resetInstance() {
88
        final MainFrame res = instance;
89
        setInstance(null);
90
        if (res != null) {
91
            res.setVisible(false);
92
            res.dispose();
93
        }
94
        return res;
95
    }
96
 
97
    private synchronized static void setInstance(MainFrame f) {
98
        assert SwingUtilities.isEventDispatchThread();
18 ilm 99
        if (f != null && instance != null)
100
            throw new IllegalStateException("More than one main frame");
101
        instance = f;
102
        if (f != null) {
103
            for (final Runnable r : runnables)
104
                r.run();
105
            runnables.clear();
106
        }
107
    }
108
 
109
    /**
110
     * Execute the runnable in the EDT after the main frame has been created. Thus if the main frame
111
     * has already been created and we're in the EDT, execute <code>r</code> immediately.
112
     *
113
     * @param r the runnable to run.
114
     * @see #getInstance()
115
     */
116
    public static void invoke(final Runnable r) {
117
        SwingThreadUtils.invoke(new Runnable() {
118
            @Override
119
            public void run() {
80 ilm 120
                if (getInstance() == null) {
18 ilm 121
                    runnables.add(r);
122
                } else {
123
                    r.run();
124
                }
125
            }
126
        });
127
    }
128
 
20 ilm 129
    private final AutoHideTabbedPane tabContainer;
130
    private TodoListPanel todoPanel;
18 ilm 131
    private JImage image;
132
 
133
    public TodoListPanel getTodoPanel() {
134
        return this.todoPanel;
135
    }
136
 
156 ilm 137
    public MainFrame(final PropsConfiguration conf) {
18 ilm 138
        super();
139
 
80 ilm 140
        this.setIconImage(new ImageIcon(MainFrame.class.getResource("frameicon.png")).getImage());
67 ilm 141
 
18 ilm 142
        Container co = this.getContentPane();
143
        co.setLayout(new GridBagLayout());
144
        GridBagConstraints c = new GridBagConstraints();
145
        c.fill = GridBagConstraints.HORIZONTAL;
146
        c.gridx = 0;
147
        c.gridy = 0;
148
        // // co.add(new RangeSlider(2005), c);
149
        c.weightx = 1;
150
        c.weighty = 0;
20 ilm 151
        this.image = new JImage(ComptaBasePropsConfiguration.class.getResource("logo.png"));
152
        this.image.setBackground(Color.WHITE);
153
        this.image.check();
154
        co.add(this.image, c);
18 ilm 155
        c.weighty = 0;
156
        c.gridy++;
157
        c.fill = GridBagConstraints.BOTH;
158
        co.add(new JSeparator(JSeparator.HORIZONTAL), c);
159
        c.gridy++;
160
        c.weighty = 1;
20 ilm 161
        this.tabContainer = new AutoHideTabbedPane();
162
        co.add(this.tabContainer, c);
163
        Dimension minSize;
164
        final String confSuffix;
165
        if (!Gestion.isMinimalMode()) {
144 ilm 166
            this.todoPanel = new TodoListPanel(UserManager.getInstance());
20 ilm 167
            this.getTabbedPane().addTab("Tâches", this.todoPanel);
168
            minSize = new Dimension(800, 600);
169
            confSuffix = "";
170
        } else {
171
            minSize = null;
172
            confSuffix = "-minimal";
173
        }
18 ilm 174
        c.weighty = 0;
175
        c.gridy++;
176
        c.fill = GridBagConstraints.HORIZONTAL;
177
        co.add(StatusPanel.getInstance(), c);
178
 
20 ilm 179
        if (minSize == null) {
180
            this.pack();
181
            minSize = new Dimension(this.getSize());
182
        }
183
        this.setMinimumSize(minSize);
184
 
80 ilm 185
        final File confFile = new File(conf.getConfDir(), "Configuration" + File.separator + "Frame" + File.separator + "mainFrame" + confSuffix + ".xml");
20 ilm 186
        new WindowStateManager(this, confFile).loadState();
187
 
18 ilm 188
        registerForMacOSXEvents();
189
 
190
        this.addWindowListener(new WindowAdapter() {
20 ilm 191
            @Override
18 ilm 192
            public void windowClosing(WindowEvent arg0) {
193
                quit();
194
            }
195
        });
196
 
197
        setInstance(this);
149 ilm 198
 
156 ilm 199
        final Thread dbConn = conf.createDBCheckThread(this, new Runnable() {
149 ilm 200
            @Override
201
            public void run() {
202
                // the user has already clicked "Quit" don't ask him again
203
                // Further the DB is unavailable so most actions (e.g. backup) cannot run
204
                UserExitPanel.exit(UserExitConf.DEFAULT);
205
            }
206
        });
207
        dbConn.start();
208
 
61 ilm 209
        // Overrive logo
80 ilm 210
        final Image im = conf instanceof ComptaPropsConfiguration ? ((ComptaPropsConfiguration) conf).getCustomLogo() : null;
61 ilm 211
        if (im != null) {
212
            image.setImage(im);
213
        }
20 ilm 214
        new NewsUpdater(this.image);
18 ilm 215
    }
216
 
73 ilm 217
    // create the UI menu from the menu manager
218
    public final void initMenuBar() {
219
        final MenuManager mm = MenuManager.getInstance();
220
        final PropertyChangeListener listener = new PropertyChangeListener() {
221
            @Override
222
            public void propertyChange(PropertyChangeEvent evt) {
223
                // TODO change only if needed (mind the actions)
224
                setJMenuBar(createMenu(mm));
225
                getLayeredPane().revalidate();
226
            }
227
        };
228
        mm.addPropertyChangeListener(listener);
229
        listener.propertyChange(null);
20 ilm 230
    }
231
 
73 ilm 232
    private final JMenuBar createMenu(final MenuManager mm) {
67 ilm 233
        final JMenuBar result = new JMenuBar();
73 ilm 234
        final Group g = mm.getGroup();
67 ilm 235
        for (int i = 0; i < g.getSize(); i++) {
236
            final Item item = g.getItem(i);
93 ilm 237
            if (item.getLocalHint().isVisible() && UserRightsManager.getCurrentUserRights().haveRight(MenuComboRightEditor.ID_RIGHT, item.getId()))
73 ilm 238
                result.add(createJMenuFrom(item, mm));
18 ilm 239
        }
28 ilm 240
        return result;
241
    }
18 ilm 242
 
73 ilm 243
    private final JMenu createJMenuFrom(final Item item, final MenuManager mm) {
244
        assert item.getLocalHint().isVisible();
67 ilm 245
        final String id = item.getId();
73 ilm 246
        final String name = mm.getLabelForId(id);
247
        final String menuLabel;
248
        final Color c;
67 ilm 249
        if (name == null || name.trim().isEmpty()) {
250
            menuLabel = id;
73 ilm 251
            c = new Color(200, 65, 20);
252
        } else {
253
            menuLabel = name;
254
            c = null;
18 ilm 255
        }
73 ilm 256
        assert menuLabel != null;
67 ilm 257
        final JMenu m = new JMenu(menuLabel);
73 ilm 258
        if (c != null)
259
            m.setForeground(c);
260
 
67 ilm 261
        if (item instanceof Group) {
73 ilm 262
            createMenuItemsRec(mm, m, Collections.<String> emptyList(), (Group) item);
93 ilm 263
        } else if (UserRightsManager.getCurrentUserRights().haveRight(MenuComboRightEditor.ID_RIGHT, item.getId())) {
73 ilm 264
            m.add(createJMenuItemForId(id, mm));
18 ilm 265
        }
67 ilm 266
        return m;
28 ilm 267
    }
18 ilm 268
 
73 ilm 269
    private final void createMenuItemsRec(final MenuManager mm, final JMenu topLevelMenu, final List<String> path, final Group g) {
270
        assert g.getLocalHint().isVisible();
271
        for (int i = 0; i < g.getSize(); i++) {
272
            final Item child = g.getItem(i);
93 ilm 273
            if (!child.getLocalHint().isVisible() || !UserRightsManager.getCurrentUserRights().haveRight(MenuComboRightEditor.ID_RIGHT, child.getId()))
73 ilm 274
                continue;
275
 
276
            if (child instanceof Group) {
277
                final List<String> newPath = new ArrayList<String>(path);
278
                newPath.add(child.getId());
279
                createMenuItemsRec(mm, topLevelMenu, newPath, (Group) child);
280
            } else {
281
                final List<String> newPath;
282
                if (path.size() % 2 == 0) {
283
                    newPath = new ArrayList<String>(path);
284
                    newPath.add(null);
285
                } else {
286
                    newPath = path;
67 ilm 287
                }
73 ilm 288
                MenuUtils.addMenuItem(createJMenuItemForId(child.getId(), mm), topLevelMenu, newPath);
67 ilm 289
            }
18 ilm 290
        }
28 ilm 291
    }
292
 
73 ilm 293
    static public String getFirstNonEmpty(final List<String> l) {
294
        for (final String s : l) {
295
            if (s != null && !s.trim().isEmpty()) {
296
                return s;
297
            }
18 ilm 298
        }
73 ilm 299
        return null;
300
    }
18 ilm 301
 
73 ilm 302
    private final JMenuItem createJMenuItemForId(final String id, final MenuManager mm) {
303
        final String mngrLabel = mm.getLabelForId(id);
304
        final Action mngrAction = mm.getActionForId(id);
305
        final String mngrActionName = mngrAction == null || mngrAction.getValue(Action.NAME) == null ? null : mngrAction.getValue(Action.NAME).toString();
306
 
307
        final String label = getFirstNonEmpty(Arrays.asList(mngrLabel, mngrActionName, id));
308
        assert label != null;
309
        final Action action;
310
        final Color fg;
311
        if (mngrAction == null) {
312
            action = new AbstractAction(label) {
67 ilm 313
                @Override
314
                public void actionPerformed(ActionEvent e) {
315
                    JOptionPane.showMessageDialog(MainFrame.this, "No action for " + id);
316
                }
317
            };
73 ilm 318
            fg = new Color(200, 65, 95);
319
        } else {
320
            action = mngrAction;
177 ilm 321
            // Allow to leave the name blank at action creation, then set it here so that it can be
322
            // used in actionPerformed()
323
            if (mngrActionName == null)
324
                action.putValue(Action.NAME, label);
73 ilm 325
            fg = label.equals(id) ? new Color(20, 65, 200) : null;
28 ilm 326
        }
18 ilm 327
 
73 ilm 328
        final JMenuItem res = new JMenuItem(action);
329
        // don't use action name but the provided label
330
        res.setHideActionText(true);
331
        res.setText(label);
332
        if (fg != null)
333
            res.setForeground(fg);
334
        return res;
18 ilm 335
    }
336
 
19 ilm 337
    public JMenuItem addMenuItem(final Action action, final String... path) {
338
        return this.addMenuItem(action, Arrays.asList(path));
339
    }
340
 
341
    /**
342
     * Adds a menu item to this menu. The path should be an alternation of menu and group within
343
     * that menu. All items within the same group will be grouped together inside separators. Menus
344
     * will be created as needed.
345
     *
346
     * @param action the action to perform.
347
     * @param path where to add the menu item.
348
     * @return the newly created item.
349
     * @throws IllegalArgumentException if path is not even.
350
     */
351
    public JMenuItem addMenuItem(final Action action, final List<String> path) throws IllegalArgumentException {
352
        if (path.size() == 0 || path.size() % 2 != 0)
353
            throw new IllegalArgumentException("Path should be of the form menu/group/menu/group/... : " + path);
354
        final JMenu topLevelMenu = getMenu(path.get(0));
355
        return MenuUtils.addMenuItem(action, topLevelMenu, path.subList(1, path.size()));
356
    }
357
 
358
    // get or create (at the end) a top level menu
359
    private JMenu getMenu(final String name) {
360
        final JMenu existing = MenuUtils.findChild(this.getJMenuBar(), name, JMenu.class);
361
        final JMenu res;
362
        if (existing == null) {
363
            res = new JMenu(name);
20 ilm 364
            // insert before the help menu
365
            this.getJMenuBar().add(res, this.getJMenuBar().getComponentCount() - 1);
19 ilm 366
        } else {
367
            res = existing;
368
        }
369
        return res;
370
    }
371
 
372
    /**
373
     * Remove the passed item from this menu. This method handles the cleanup of separators and
374
     * empty menus.
375
     *
376
     * @param item the item to remove.
377
     * @throws IllegalArgumentException if <code>item</code> is not in this menu.
378
     */
379
    public void removeMenuItem(final JMenuItem item) throws IllegalArgumentException {
380
        if (SwingThreadUtils.getAncestorOrSelf(JMenuBar.class, item) != this.getJMenuBar())
381
            throw new IllegalArgumentException("Item not in this menu " + item);
382
        MenuUtils.removeMenuItem(item);
383
    }
384
 
18 ilm 385
    // Generic registration with the Mac OS X application menu
386
    // Checks the platform, then attempts to register with the Apple EAWT
387
    // See OSXAdapter.java to see how this is done without directly referencing any Apple APIs
388
    public void registerForMacOSXEvents() {
389
        if (Gestion.MAC_OS_X) {
390
            try {
391
                // Generate and register the OSXAdapter, passing it a hash of all the methods we
392
                // wish to use as delegates for various com.apple.eawt.ApplicationListener methods
393
                OSXAdapter.setQuitHandler(this, getClass().getDeclaredMethod("quit", new Class[0]));
394
                OSXAdapter.setAboutHandler(this, getClass().getDeclaredMethod("about", new Class[0]));
395
                OSXAdapter.setPreferencesHandler(this, getClass().getDeclaredMethod("preferences", new Class[0]));
396
                // no OSXAdapter.setFileHandler() for now
397
            } catch (Exception e) {
398
                System.err.println("Error while loading the OSXAdapter:");
399
                e.printStackTrace();
400
            }
401
        }
402
    }
403
 
404
    // used by OSXAdapter
405
    public final void preferences() {
156 ilm 406
        MenuManager.getInstance().getActionForId("preferences").actionPerformed(null);
18 ilm 407
    }
408
 
409
    public final void about() {
177 ilm 410
        MenuManager.getInstance().getActionForId("information").actionPerformed(null);
18 ilm 411
    }
412
 
413
    public boolean quit() {
20 ilm 414
        if (this.getTodoPanel() != null)
415
            this.getTodoPanel().stopUpdate();
18 ilm 416
        Gestion.askForExit();
417
        return false;
418
    }
20 ilm 419
 
420
    public final AutoHideTabbedPane getTabbedPane() {
421
        return this.tabContainer;
422
    }
18 ilm 423
}