OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

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