public static void reloadFonts() {
    String font1Path = Config.getString("font1_file");
    String font2Path = Config.getString("font2_file");

    int font1Type = font1Path.endsWith(".ttf") ? Font.TRUETYPE_FONT : Font.TYPE1_FONT;
    int font2Type = font2Path.endsWith(".ttf") ? Font.TRUETYPE_FONT : Font.TYPE1_FONT;

    File font1 = new File(font1Path);
    File font2 = new File(font2Path);

    try {
      fonts =
          new Font[] {
            Font.createFont(font1Type, font1)
                .deriveFont(Font.BOLD, Integer.parseInt(Config.getString("font1_size"))),
            Font.createFont(font2Type, font2)
                .deriveFont(Font.BOLD, Integer.parseInt(Config.getString("font2_size"))),
          };
    } catch (IOException | FontFormatException e) {
      JOptionPane.showMessageDialog(
          instance,
          "Font failed to load: " + e,
          Constants.SOFTWARE_NAME,
          JOptionPane.WARNING_MESSAGE);
      e.printStackTrace();
    }
  }
    ChooseFontPanel(String name, String fontPathKey, String fontSizeKey) {
      this.fontPathKey = fontPathKey;
      this.fontSizeKey = fontSizeKey;

      originalPath = Config.getString(fontPathKey);
      originalSize = Config.getInteger(fontSizeKey);

      setBorder(BorderFactory.createTitledBorder(BorderFactory.createEmptyBorder(), name));
      setLayout(new BorderLayout());

      final File file = new File(originalPath);

      add(
          pathButton =
              new PainlessButton(getOpenIcon()) {
                {
                  addActionListener(
                      new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent ae) {
                          if (chooser.showOpenDialog(ChooseFontPanel.this.getParent())
                              == JFileChooser.APPROVE_OPTION) {
                            pathField.setText(chooser.getSelectedFile().getAbsolutePath());
                          }
                        }
                      });
                }
              },
          BorderLayout.WEST);
      add(
          pathField =
              new GhostTextField("File path") {
                {
                  setEditable(false);
                  setText(file.getAbsolutePath());
                }
              },
          BorderLayout.CENTER);
      add(
          sizeSpinner =
              new JSpinner() {
                {
                  setModel(new SpinnerNumberModel(originalSize, 8, 800, 1));
                  setEditor(new JSpinner.NumberEditor(this, "#.#"));
                }

                @Override
                public Dimension getPreferredSize() {
                  return new Dimension(2 * getHeight(), getHeight());
                }
              },
          BorderLayout.EAST);
    }
 @Override
 public void run() {
   if (!originalPath.equals(pathField.getText())
       || originalSize != ((Integer) sizeSpinner.getValue())) {
     ConcurrentHashMap map = new ConcurrentHashMap();
     map.put(fontPathKey, pathField.getText());
     map.put(fontSizeKey, sizeSpinner.getValue());
     Config.put(map);
   }
 }
    @Override
    public void run() {
      int rgb = -1;

      try {
        rgb = Integer.decode(colorField.getText());
      } catch (NumberFormatException nfe) {
      }

      if ((originalPath != null && !originalPath.equals(colorField.getText()))
          || (originalColor != null && (originalColor.getRGB() & 0xFFFFFF) != rgb)) {
        Config.put(backgroundKey, colorField.getText());
      }
    }
    ChooseNumberPanel(
        String name, final double minimumValue, final double maximumValue, final String keyName) {
      setBorder(BorderFactory.createTitledBorder(BorderFactory.createEmptyBorder(), name));
      setLayout(new BoxLayout(this, BoxLayout.X_AXIS));

      originalValue = Config.getDouble(keyName);

      this.keyName = keyName;
      this.minimumValue = minimumValue;
      this.maximumValue = maximumValue;
      gap = maximumValue - minimumValue;

      add(
          numberSlider =
              new JSlider() {
                {
                  setMinimum(normalize(minimumValue));
                  setMaximum(normalize(maximumValue));
                  setValue(normalize(originalValue));
                  setPaintLabels(true);
                  setPaintTrack(true);
                  setAlignmentX(CENTER_ALIGNMENT);

                  addChangeListener(
                      new ChangeListener() {
                        @Override
                        public void stateChanged(ChangeEvent ce) {
                          isChanged = true;
                          setNumber(getValue());
                        }
                      });
                }
              });

      add(
          numberField =
              new GhostTextField(null) {
                {
                  setColumns(5);
                  setEditable(false);
                  setOpaque(false);
                  setAlignmentX(CENTER_ALIGNMENT);
                }
              });

      setNumber(normalize(originalValue));
    }
  static {
    try {
      UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

      Enumeration keys = UIManager.getDefaults().keys();
      while (keys.hasMoreElements()) {
        Object key = keys.nextElement();
        Object value = UIManager.get(key);
        if (value instanceof Font) {
          Font f = (Font) value;
          UIManager.put(key, f.deriveFont(Font.BOLD, f.getSize() * 1.2f));
        }
      }
    } catch (ClassNotFoundException
        | InstantiationException
        | IllegalAccessException
        | UnsupportedLookAndFeelException e) {
      e.printStackTrace();
    }

    localGraphicsEnvironment = GraphicsEnvironment.getLocalGraphicsEnvironment();
    defaultScreenDevice = localGraphicsEnvironment.getDefaultScreenDevice();
    displayMode = defaultScreenDevice.getDisplayMode();

    for (GraphicsDevice device : localGraphicsEnvironment.getScreenDevices()) {
      System.out.println(device.toString());
    }

    reloadFonts();
    Config.addConfigChangeListener(
        new String[] {
          "font1_file", "font1_size", "font2_file", "font2_size",
        },
        new ConfigChangeListener() {
          @Override
          public void configChanged(ConfigChangeEvent e) {
            reloadFonts();

            MainFrame.getInstance().repaint();
            ProgramWindow.getInstance().repaint();
          }
        });

    instance = new MainFrame();
  }
  public static Object getBackgroundObject() {
    if (background != null) {
      return background;
    }

    String backgroundString = Config.getString("background");

    try {
      int bgRgb = Integer.decode(backgroundString);
      return background = new Color(bgRgb);
    } catch (NumberFormatException e) {
      try {
        return background = ImageIO.read(new File(backgroundString));
      } catch (IOException ioe) {
        ioe.printStackTrace();
        return null;
      }
    }
  }
 public static GraphicsDevice getSelectedGraphicsDevice() {
   int displayDeviceIndex = Integer.parseInt(Config.getString("display_device"));
   return localGraphicsEnvironment.getScreenDevices()[displayDeviceIndex];
 }
 @Override
 public void run() {
   if (isChanged) {
     Config.put(keyName, getValue());
   }
 }
    ChooseBackgroundPanel(String name, String backgroundKey) {
      this.backgroundKey = backgroundKey;

      String s = Config.getString(backgroundKey);

      Color _originalColor = null;
      String _originalPath = null;

      try {
        _originalColor = new Color(Integer.decode(s));
        _originalPath = null;
      } catch (NumberFormatException nfe) {
        _originalColor = null;
        _originalPath = s;
      } finally {
        originalColor = _originalColor;
        originalPath = _originalPath;
      }

      setBorder(BorderFactory.createTitledBorder(BorderFactory.createEmptyBorder(), name));
      setLayout(new BorderLayout());

      add(
          pathButton =
              new PainlessButton(getOpenIcon()) {
                {
                  addActionListener(
                      new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent ae) {
                          if (chooser.showOpenDialog(ChooseBackgroundPanel.this.getParent())
                              == JFileChooser.APPROVE_OPTION) {
                            colorField.setText(chooser.getSelectedFile().getAbsolutePath());
                          }
                        }
                      });
                }
              },
          BorderLayout.WEST);
      add(
          colorField =
              new GhostTextField("File path / Color hex") {
                {
                  setEditable(false);
                  if (originalColor != null) {
                    colorChooserPanel.getColorSelectionModel().setSelectedColor(originalColor);
                    setText(getColorString(originalColor));
                  } else if (originalPath != null) {
                    setText(originalPath);
                  }
                }
              },
          BorderLayout.CENTER);
      add(
          colorButton =
              new PainlessButton(null) {
                BufferedImage rainbow;

                {
                  addActionListener(
                      new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent ae) {
                          int returnValue =
                              JOptionPane.showOptionDialog(
                                  ChooseBackgroundPanel.this.getParent(),
                                  colorChooserPanel,
                                  "Choose background color",
                                  JOptionPane.DEFAULT_OPTION,
                                  JOptionPane.PLAIN_MESSAGE,
                                  null,
                                  new String[] {"Ok"},
                                  null);

                          if (returnValue == JOptionPane.OK_OPTION) {
                            Color c = colorChooserPanel.getColorSelectionModel().getSelectedColor();
                            if (c != null) {
                              colorField.setText(getColorString(c));
                            }
                          }
                        }
                      });
                }

                @Override
                protected void paintComponent(Graphics g2d) {
                  super.paintComponent(g2d);

                  int w = getWidth();
                  int h = getHeight();

                  if (rainbow == null || rainbow.getWidth() != w || rainbow.getHeight() != h) {
                    rainbow = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);

                    int[] arr = new int[w * h];
                    int i = 0;

                    for (int j = 0; j < h; j++) {
                      int r = (j * 255) / (h - 1);
                      for (int k = 0; k < w; k++) {
                        int g = (k * 255) / (w - 1);
                        int b = 128;
                        arr[i++] = (r << 16) | (g << 8) | b;
                      }
                    }

                    rainbow.setRGB(0, 0, w, h, arr, 0, w);
                  }

                  g2d.drawImage(rainbow, 0, 0, this);
                }
              },
          BorderLayout.EAST);
    }