/**
   * Prompts the user with a list of WMS layers which can be adjusted
   *
   * @param adjustableLayers the list of adjustable layers
   * @return the selected layer; null, if no layer was selected
   */
  protected Layer askAdjustLayer(List<? extends Layer> adjustableLayers) {
    if (adjustableLayers.size() == 0) return null;
    if (adjustableLayers.size() == 1) return adjustableLayers.get(0);
    JComboBox<Layer> layerList = new JComboBox<>();
    layerList.setRenderer(new LayerListCellRenderer());
    layerList.setModel(new DefaultComboBoxModel<>(adjustableLayers.toArray(new Layer[0])));
    layerList.setSelectedIndex(0);

    JPanel pnl = new JPanel();
    pnl.setLayout(new GridBagLayout());
    pnl.add(new JLabel(tr("Please select the imagery layer to adjust.")), GBC.eol());
    pnl.add(layerList, GBC.eol());

    ExtendedDialog diag =
        new ExtendedDialog(
            Main.parent,
            tr("Select imagery layer"),
            new String[] {tr("Start adjusting"), tr("Cancel")});
    diag.setContent(pnl);
    diag.setButtonIcons(new String[] {"mapmode/adjustimg", "cancel"});
    diag.showDialog();
    int decision = diag.getValue();
    if (decision != 1) return null;
    Layer adjustLayer = (Layer) layerList.getSelectedItem();
    return adjustLayer;
  }
Esempio n. 2
0
 public static boolean confirmOverwrite(File file) {
   if (file == null || (file.exists())) {
     ExtendedDialog dialog =
         new ExtendedDialog(
             Main.parent, tr("Overwrite"), new String[] {tr("Overwrite"), tr("Cancel")});
     dialog.setContent(tr("File exists. Overwrite?"));
     dialog.setButtonIcons(new String[] {"save_as.png", "cancel.png"});
     dialog.showDialog();
     return (dialog.getValue() == 1);
   }
   return true;
 }
Esempio n. 3
0
  public void actionPerformed(ActionEvent e) {

    JCheckBox layer = new JCheckBox(tr("Separate Layer"));
    layer.setToolTipText(tr("Select if the data should be downloaded into a new layer"));
    layer.setSelected(Main.pref.getBoolean("download.newlayer"));
    JPanel all = new JPanel(new GridBagLayout());
    GridBagConstraints gc = new GridBagConstraints();
    gc.fill = GridBagConstraints.HORIZONTAL;
    gc.weightx = 1.0;
    gc.anchor = GridBagConstraints.FIRST_LINE_START;
    all.add(new JLabel(tr("Enter URL to download:")), gc);
    HistoryComboBox uploadAdresses = new HistoryComboBox();
    uploadAdresses.setToolTipText(tr("Enter an URL from where data should be downloaded"));
    restoreUploadAddressHistory(uploadAdresses);
    gc.gridy = 1;
    all.add(uploadAdresses, gc);
    gc.gridy = 2;
    gc.fill = GridBagConstraints.BOTH;
    gc.weighty = 1.0;
    all.add(layer, gc);
    ExtendedDialog dialog =
        new ExtendedDialog(
            Main.parent, tr("Download Location"), new String[] {tr("Download URL"), tr("Cancel")});
    dialog.setContent(all, false /* don't embedded content in JScrollpane  */);
    dialog.setButtonIcons(new String[] {"download.png", "cancel.png"});
    dialog.setToolTipTexts(
        new String[] {tr("Start downloading data"), tr("Close dialog and cancel downloading")});
    dialog.configureContextsensitiveHelp("/Action/OpenLocation", true /* show help button */);
    dialog.showDialog();
    if (dialog.getValue() != 1) return;
    remindUploadAddressHistory(uploadAdresses);
    openUrl(layer.isSelected(), uploadAdresses.getText());
  }
Esempio n. 4
0
 @Override
 public void setVisible(boolean visible) {
   super.setVisible(visible);
   if (visible) {
     list.setSelectedIndex(list.getSelectedIndex()); // highlight way segments
   } else {
     setHighlightedWaySegments(Collections.<WaySegment>emptyList());
   }
 }
Esempio n. 5
0
 @Override
 protected void buttonAction(int buttonIndex, ActionEvent evt) {
   super.buttonAction(buttonIndex, evt);
   if (getValue() == 1) {
     final Way wayToKeep = list.getSelectedValue();
     final SplitWayResult result =
         doSplitWay(getEditLayer(), selectedWay, wayToKeep, newWays, selection);
     Main.main.undoRedo.add(result.getCommand());
     getCurrentDataSet().setSelected(result.getNewSelection());
   }
 }
Esempio n. 6
0
  protected Layer askTargetLayer(List<Layer> targetLayers) {
    JosmComboBox layerList = new JosmComboBox(targetLayers.toArray());
    layerList.setRenderer(new LayerListCellRenderer());
    layerList.setSelectedIndex(0);

    JPanel pnl = new JPanel(new GridBagLayout());
    pnl.add(new JLabel(tr("Please select the target layer.")), GBC.eol());
    pnl.add(layerList, GBC.eol());

    ExtendedDialog ed =
        new ExtendedDialog(
            Main.parent, tr("Select target layer"), new String[] {tr("Merge"), tr("Cancel")});
    ed.setButtonIcons(new String[] {"dialogs/mergedown", "cancel"});
    ed.setContent(pnl);
    ed.showDialog();
    if (ed.getValue() != 1) return null;

    Layer targetLayer = (Layer) layerList.getSelectedItem();
    return targetLayer;
  }
Esempio n. 7
0
 /**
  * Warns the user if a cyclic dependency is detected
  *
  * @param e the cyclic dependency exception
  */
 protected void warnCyclicUploadDependency(CyclicUploadDependencyException e) {
   List<Relation> dep = e.getCyclicUploadDependency();
   Relation last = dep.get(dep.size() - 1);
   Iterator<Relation> it = dep.iterator();
   while (it.hasNext()) {
     if (it.next() != last) {
       it.remove();
     } else {
       break;
     }
   }
   JPanel pnl = buildWarningPanel(dep);
   ExtendedDialog dialog =
       new ExtendedDialog(Main.parent, tr("Cycling dependencies"), new String[] {tr("OK")});
   dialog.setContent(pnl, false /* don't embed in scroll pane */);
   dialog.setButtonIcons(new String[] {"ok"});
   dialog.setRememberWindowGeometry(
       getClass().getName() + ".geometry",
       WindowGeometry.centerInWindow(Main.parent, new Dimension(300, 300)));
   dialog.showDialog();
 }
Esempio n. 8
0
  @Override
  public void actionPerformed(ActionEvent arg0) {
    // Construct the list of loaded GPX tracks
    Collection<Layer> layerLst = Main.map.mapView.getAllLayers();
    GpxDataWrapper defaultItem = null;
    for (Layer cur : layerLst) {
      if (cur instanceof GpxLayer) {
        GpxLayer curGpx = (GpxLayer) cur;
        GpxDataWrapper gdw =
            new GpxDataWrapper(curGpx.getName(), curGpx.data, curGpx.data.storageFile);
        gpxLst.add(gdw);
        if (cur == yLayer.gpxLayer) {
          defaultItem = gdw;
        }
      }
    }
    for (GpxData data : loadedGpxData) {
      gpxLst.add(new GpxDataWrapper(data.storageFile.getName(), data, data.storageFile));
    }

    if (gpxLst.isEmpty()) {
      gpxLst.add(new GpxDataWrapper(tr("<No GPX track loaded yet>"), null, null));
    }

    JPanel panelCb = new JPanel();

    panelCb.add(new JLabel(tr("GPX track: ")));

    cbGpx = new JosmComboBox<>(gpxLst.toArray(new GpxDataWrapper[0]));
    if (defaultItem != null) {
      cbGpx.setSelectedItem(defaultItem);
    }
    cbGpx.addActionListener(statusBarUpdaterWithRepaint);
    panelCb.add(cbGpx);

    JButton buttonOpen = new JButton(tr("Open another GPX trace"));
    buttonOpen.addActionListener(new LoadGpxDataActionListener());
    panelCb.add(buttonOpen);

    JPanel panelTf = new JPanel(new GridBagLayout());

    String prefTimezone = Main.pref.get("geoimage.timezone", "0:00");
    if (prefTimezone == null) {
      prefTimezone = "0:00";
    }
    try {
      timezone = parseTimezone(prefTimezone);
    } catch (ParseException e) {
      timezone = 0;
    }

    tfTimezone = new JosmTextField(10);
    tfTimezone.setText(formatTimezone(timezone));

    try {
      delta = parseOffset(Main.pref.get("geoimage.delta", "0"));
    } catch (ParseException e) {
      delta = 0;
    }
    delta = delta / 1000; // milliseconds -> seconds

    tfOffset = new JosmTextField(10);
    tfOffset.setText(Long.toString(delta));

    JButton buttonViewGpsPhoto =
        new JButton(
            tr("<html>Use photo of an accurate clock,<br>" + "e.g. GPS receiver display</html>"));
    buttonViewGpsPhoto.setIcon(ImageProvider.get("clock"));
    buttonViewGpsPhoto.addActionListener(new SetOffsetActionListener());

    JButton buttonAutoGuess = new JButton(tr("Auto-Guess"));
    buttonAutoGuess.setToolTipText(tr("Matches first photo with first gpx point"));
    buttonAutoGuess.addActionListener(new AutoGuessActionListener());

    JButton buttonAdjust = new JButton(tr("Manual adjust"));
    buttonAdjust.addActionListener(new AdjustActionListener());

    JLabel labelPosition = new JLabel(tr("Override position for: "));

    int numAll = getSortedImgList(true, true).size();
    int numExif = numAll - getSortedImgList(false, true).size();
    int numTagged = numAll - getSortedImgList(true, false).size();

    cbExifImg =
        new JCheckBox(tr("Images with geo location in exif data ({0}/{1})", numExif, numAll));
    cbExifImg.setEnabled(numExif != 0);

    cbTaggedImg =
        new JCheckBox(tr("Images that are already tagged ({0}/{1})", numTagged, numAll), true);
    cbTaggedImg.setEnabled(numTagged != 0);

    labelPosition.setEnabled(cbExifImg.isEnabled() || cbTaggedImg.isEnabled());

    boolean ticked = yLayer.thumbsLoaded || Main.pref.getBoolean("geoimage.showThumbs", false);
    cbShowThumbs = new JCheckBox(tr("Show Thumbnail images on the map"), ticked);
    cbShowThumbs.setEnabled(!yLayer.thumbsLoaded);

    int y = 0;
    GBC gbc = GBC.eol();
    gbc.gridx = 0;
    gbc.gridy = y++;
    panelTf.add(panelCb, gbc);

    gbc = GBC.eol().fill(GBC.HORIZONTAL).insets(0, 0, 0, 12);
    gbc.gridx = 0;
    gbc.gridy = y++;
    panelTf.add(new JSeparator(SwingConstants.HORIZONTAL), gbc);

    gbc = GBC.std();
    gbc.gridx = 0;
    gbc.gridy = y;
    panelTf.add(new JLabel(tr("Timezone: ")), gbc);

    gbc = GBC.std().fill(GBC.HORIZONTAL);
    gbc.gridx = 1;
    gbc.gridy = y++;
    gbc.weightx = 1.;
    panelTf.add(tfTimezone, gbc);

    gbc = GBC.std();
    gbc.gridx = 0;
    gbc.gridy = y;
    panelTf.add(new JLabel(tr("Offset:")), gbc);

    gbc = GBC.std().fill(GBC.HORIZONTAL);
    gbc.gridx = 1;
    gbc.gridy = y++;
    gbc.weightx = 1.;
    panelTf.add(tfOffset, gbc);

    gbc = GBC.std().insets(5, 5, 5, 5);
    gbc.gridx = 2;
    gbc.gridy = y - 2;
    gbc.gridheight = 2;
    gbc.gridwidth = 2;
    gbc.fill = GridBagConstraints.BOTH;
    gbc.weightx = 0.5;
    panelTf.add(buttonViewGpsPhoto, gbc);

    gbc = GBC.std().fill(GBC.BOTH).insets(5, 5, 5, 5);
    gbc.gridx = 2;
    gbc.gridy = y++;
    gbc.weightx = 0.5;
    panelTf.add(buttonAutoGuess, gbc);

    gbc.gridx = 3;
    panelTf.add(buttonAdjust, gbc);

    gbc = GBC.eol().fill(GBC.HORIZONTAL).insets(0, 12, 0, 0);
    gbc.gridx = 0;
    gbc.gridy = y++;
    panelTf.add(new JSeparator(SwingConstants.HORIZONTAL), gbc);

    gbc = GBC.eol();
    gbc.gridx = 0;
    gbc.gridy = y++;
    panelTf.add(labelPosition, gbc);

    gbc = GBC.eol();
    gbc.gridx = 1;
    gbc.gridy = y++;
    panelTf.add(cbExifImg, gbc);

    gbc = GBC.eol();
    gbc.gridx = 1;
    gbc.gridy = y++;
    panelTf.add(cbTaggedImg, gbc);

    gbc = GBC.eol();
    gbc.gridx = 0;
    gbc.gridy = y++;
    panelTf.add(cbShowThumbs, gbc);

    final JPanel statusBar = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
    statusBar.setBorder(BorderFactory.createLoweredBevelBorder());
    statusBarText = new JLabel(" ");
    statusBarText.setFont(statusBarText.getFont().deriveFont(8));
    statusBar.add(statusBarText);

    tfTimezone.addFocusListener(repaintTheMap);
    tfOffset.addFocusListener(repaintTheMap);

    tfTimezone.getDocument().addDocumentListener(statusBarUpdater);
    tfOffset.getDocument().addDocumentListener(statusBarUpdater);
    cbExifImg.addItemListener(statusBarUpdaterWithRepaint);
    cbTaggedImg.addItemListener(statusBarUpdaterWithRepaint);

    statusBarUpdater.updateStatusBar();

    outerPanel = new JPanel(new BorderLayout());
    outerPanel.add(statusBar, BorderLayout.PAGE_END);

    if (!GraphicsEnvironment.isHeadless()) {
      syncDialog =
          new ExtendedDialog(
              Main.parent,
              tr("Correlate images with GPX track"),
              new String[] {tr("Correlate"), tr("Cancel")},
              false);
      syncDialog.setContent(panelTf, false);
      syncDialog.setButtonIcons(new String[] {"ok", "cancel"});
      syncDialog.setupDialog();
      outerPanel.add(syncDialog.getContentPane(), BorderLayout.PAGE_START);
      syncDialog.setContentPane(outerPanel);
      syncDialog.pack();
      syncDialog.addWindowListener(new SyncDialogWindowListener());
      syncDialog.showDialog();
    }
  }
Esempio n. 9
0
  @Override
  public void actionPerformed(ActionEvent e) {
    if (!isEnabled()) return;

    Collection<OsmPrimitive> sel = getCurrentDataSet().getAllSelected();
    layer = Main.map.mapView.getEditLayer();

    toPurge = new HashSet<OsmPrimitive>(sel);
    toPurgeAdditionally = new ArrayList<OsmPrimitive>();
    toPurgeChecked = new HashSet<OsmPrimitive>();

    // Add referrer, unless the object to purge is not new
    // and the parent is a relation
    HashSet<OsmPrimitive> toPurgeRecursive = new HashSet<OsmPrimitive>();
    while (!toPurge.isEmpty()) {

      for (OsmPrimitive osm : toPurge) {
        for (OsmPrimitive parent : osm.getReferrers()) {
          if (toPurge.contains(parent)
              || toPurgeChecked.contains(parent)
              || toPurgeRecursive.contains(parent)) {
            continue;
          }
          if (parent instanceof Way || (parent instanceof Relation && osm.isNew())) {
            toPurgeAdditionally.add(parent);
            toPurgeRecursive.add(parent);
          }
        }
        toPurgeChecked.add(osm);
      }
      toPurge = toPurgeRecursive;
      toPurgeRecursive = new HashSet<OsmPrimitive>();
    }

    makeIncomplete = new HashSet<OsmPrimitive>();

    // Find the objects that will be incomplete after purging.
    // At this point, all parents of new to-be-purged primitives are
    // also to-be-purged and
    // all parents of not-new to-be-purged primitives are either
    // to-be-purged or of type relation.
    TOP:
    for (OsmPrimitive child : toPurgeChecked) {
      if (child.isNew()) {
        continue;
      }
      for (OsmPrimitive parent : child.getReferrers()) {
        if (parent instanceof Relation && !toPurgeChecked.contains(parent)) {
          makeIncomplete.add(child);
          continue TOP;
        }
      }
    }

    // Add untagged way nodes. Do not add nodes that have other
    // referrers not yet to-be-purged.
    if (Main.pref.getBoolean("purge.add_untagged_waynodes", true)) {
      Set<OsmPrimitive> wayNodes = new HashSet<OsmPrimitive>();
      for (OsmPrimitive osm : toPurgeChecked) {
        if (osm instanceof Way) {
          Way w = (Way) osm;
          NODE:
          for (Node n : w.getNodes()) {
            if (n.isTagged() || toPurgeChecked.contains(n)) {
              continue;
            }
            for (OsmPrimitive ref : n.getReferrers()) {
              if (ref != w && !toPurgeChecked.contains(ref)) {
                continue NODE;
              }
            }
            wayNodes.add(n);
          }
        }
      }
      toPurgeChecked.addAll(wayNodes);
      toPurgeAdditionally.addAll(wayNodes);
    }

    if (Main.pref.getBoolean("purge.add_relations_with_only_incomplete_members", true)) {
      Set<Relation> relSet = new HashSet<Relation>();
      for (OsmPrimitive osm : toPurgeChecked) {
        for (OsmPrimitive parent : osm.getReferrers()) {
          if (parent instanceof Relation
              && !(toPurgeChecked.contains(parent))
              && hasOnlyIncompleteMembers((Relation) parent, toPurgeChecked, relSet)) {
            relSet.add((Relation) parent);
          }
        }
      }

      /** Add higher level relations (list gets extended while looping over it) */
      List<Relation> relLst = new ArrayList<Relation>(relSet);
      for (int i = 0; i < relLst.size(); ++i) {
        for (OsmPrimitive parent : relLst.get(i).getReferrers()) {
          if (!(toPurgeChecked.contains(parent))
              && hasOnlyIncompleteMembers((Relation) parent, toPurgeChecked, relLst)) {
            relLst.add((Relation) parent);
          }
        }
      }
      relSet = new HashSet<Relation>(relLst);
      toPurgeChecked.addAll(relSet);
      toPurgeAdditionally.addAll(relSet);
    }

    boolean modified = false;
    for (OsmPrimitive osm : toPurgeChecked) {
      if (osm.isModified()) {
        modified = true;
        break;
      }
    }

    ExtendedDialog confirmDlg =
        new ExtendedDialog(
            Main.parent, tr("Confirm Purging"), new String[] {tr("Purge"), tr("Cancel")});
    confirmDlg.setContent(buildPanel(modified), false);
    confirmDlg.setButtonIcons(new String[] {"ok", "cancel"});

    int answer = confirmDlg.showDialog().getValue();
    if (answer != 1) return;

    Main.pref.put("purge.clear_undo_redo", cbClearUndoRedo.isSelected());

    Main.main.undoRedo.add(
        new PurgeCommand(Main.map.mapView.getEditLayer(), toPurgeChecked, makeIncomplete));

    if (cbClearUndoRedo.isSelected()) {
      Main.main.undoRedo.clean();
      getCurrentDataSet().clearSelectionHistory();
    }
  }
Esempio n. 10
0
  /**
   * Called when the action is executed.
   *
   * <p>This method performs an expensive check whether the selection clearly defines one of the
   * split actions outlined above, and if yes, calls the splitWay method.
   */
  @Override
  public void actionPerformed(ActionEvent e) {

    Collection<OsmPrimitive> selection = getCurrentDataSet().getSelected();

    List<Node> selectedNodes = OsmPrimitive.getFilteredList(selection, Node.class);
    List<Way> selectedWays = OsmPrimitive.getFilteredList(selection, Way.class);
    List<Relation> selectedRelations = OsmPrimitive.getFilteredList(selection, Relation.class);
    List<Way> applicableWays = getApplicableWays(selectedWays, selectedNodes);

    if (applicableWays == null) {
      new Notification(
              tr("The current selection cannot be used for splitting - no node is selected."))
          .setIcon(JOptionPane.WARNING_MESSAGE)
          .show();
      return;
    } else if (applicableWays.isEmpty()) {
      new Notification(tr("The selected nodes do not share the same way."))
          .setIcon(JOptionPane.WARNING_MESSAGE)
          .show();
      return;
    }

    // If several ways have been found, remove ways that doesn't have selected
    // node in the middle
    if (applicableWays.size() > 1) {
      for (Iterator<Way> it = applicableWays.iterator(); it.hasNext(); ) {
        Way w = it.next();
        for (Node n : selectedNodes) {
          if (!w.isInnerNode(n)) {
            it.remove();
            break;
          }
        }
      }
    }

    if (applicableWays.isEmpty()) {
      new Notification(
              trn(
                  "The selected node is not in the middle of any way.",
                  "The selected nodes are not in the middle of any way.",
                  selectedNodes.size()))
          .setIcon(JOptionPane.WARNING_MESSAGE)
          .show();
      return;
    } else if (applicableWays.size() > 1) {
      new Notification(
              trn(
                  "There is more than one way using the node you selected. Please select the way also.",
                  "There is more than one way using the nodes you selected. Please select the way also.",
                  selectedNodes.size()))
          .setIcon(JOptionPane.WARNING_MESSAGE)
          .show();
      return;
    }

    // Finally, applicableWays contains only one perfect way
    final Way selectedWay = applicableWays.get(0);
    final List<List<Node>> wayChunks = buildSplitChunks(selectedWay, selectedNodes);
    if (wayChunks != null) {
      final List<OsmPrimitive> sel =
          new ArrayList<>(selectedWays.size() + selectedRelations.size());
      sel.addAll(selectedWays);
      sel.addAll(selectedRelations);

      final List<Way> newWays = createNewWaysFromChunks(selectedWay, wayChunks);
      final Way wayToKeep = determineWayToKeep(newWays);

      if (ExpertToggleAction.isExpert() && !selectedWay.isNew()) {
        final ExtendedDialog dialog =
            new SegmentToKeepSelectionDialog(selectedWay, newWays, wayToKeep, sel);
        dialog.setModal(false);
        dialog.showDialog();
      } else {
        final SplitWayResult result =
            doSplitWay(getEditLayer(), selectedWay, wayToKeep, newWays, sel);
        Main.main.undoRedo.add(result.getCommand());
        getCurrentDataSet().setSelected(result.getNewSelection());
      }
    }
  }
Esempio n. 11
0
  public static SearchSetting showSearchDialog(SearchSetting initialValues) {
    if (initialValues == null) {
      initialValues = new SearchSetting();
    }
    // -- prepare the combo box with the search expressions
    //
    JLabel label =
        new JLabel(initialValues instanceof Filter ? tr("Filter string:") : tr("Search string:"));
    final HistoryComboBox hcbSearchString = new HistoryComboBox();
    final String tooltip = tr("Enter the search expression");
    hcbSearchString.setText(initialValues.text);
    hcbSearchString.setToolTipText(tooltip);
    // we have to reverse the history, because ComboBoxHistory will reverse it again in addElement()
    //
    List<String> searchExpressionHistory = getSearchExpressionHistory();
    Collections.reverse(searchExpressionHistory);
    hcbSearchString.setPossibleItems(searchExpressionHistory);
    hcbSearchString.setPreferredSize(new Dimension(40, hcbSearchString.getPreferredSize().height));
    label.setLabelFor(hcbSearchString);

    JRadioButton replace =
        new JRadioButton(tr("replace selection"), initialValues.mode == SearchMode.replace);
    JRadioButton add =
        new JRadioButton(tr("add to selection"), initialValues.mode == SearchMode.add);
    JRadioButton remove =
        new JRadioButton(tr("remove from selection"), initialValues.mode == SearchMode.remove);
    JRadioButton inSelection =
        new JRadioButton(tr("find in selection"), initialValues.mode == SearchMode.in_selection);
    ButtonGroup bg = new ButtonGroup();
    bg.add(replace);
    bg.add(add);
    bg.add(remove);
    bg.add(inSelection);

    final JCheckBox caseSensitive =
        new JCheckBox(tr("case sensitive"), initialValues.caseSensitive);
    JCheckBox allElements = new JCheckBox(tr("all objects"), initialValues.allElements);
    allElements.setToolTipText(tr("Also include incomplete and deleted objects in search."));
    final JRadioButton standardSearch =
        new JRadioButton(tr("standard"), !initialValues.regexSearch && !initialValues.mapCSSSearch);
    final JRadioButton regexSearch =
        new JRadioButton(tr("regular expression"), initialValues.regexSearch);
    final JRadioButton mapCSSSearch =
        new JRadioButton(tr("MapCSS selector"), initialValues.mapCSSSearch);
    final JCheckBox addOnToolbar = new JCheckBox(tr("add toolbar button"), false);
    final ButtonGroup bg2 = new ButtonGroup();
    bg2.add(standardSearch);
    bg2.add(regexSearch);
    bg2.add(mapCSSSearch);

    JPanel top = new JPanel(new GridBagLayout());
    top.add(label, GBC.std().insets(0, 0, 5, 0));
    top.add(hcbSearchString, GBC.eol().fill(GBC.HORIZONTAL));
    JPanel left = new JPanel(new GridBagLayout());
    left.add(replace, GBC.eol());
    left.add(add, GBC.eol());
    left.add(remove, GBC.eol());
    left.add(inSelection, GBC.eop());
    left.add(caseSensitive, GBC.eol());
    if (Main.pref.getBoolean("expert", false)) {
      left.add(allElements, GBC.eol());
      left.add(addOnToolbar, GBC.eop());
      left.add(standardSearch, GBC.eol());
      left.add(regexSearch, GBC.eol());
      left.add(mapCSSSearch, GBC.eol());
    }

    final JPanel right;
    right = new JPanel(new GridBagLayout());
    buildHints(right, hcbSearchString);

    final JTextComponent editorComponent = hcbSearchString.getEditorComponent();
    editorComponent
        .getDocument()
        .addDocumentListener(
            new AbstractTextComponentValidator(editorComponent) {

              @Override
              public void validate() {
                if (!isValid()) {
                  feedbackInvalid(tr("Invalid search expression"));
                } else {
                  feedbackValid(tooltip);
                }
              }

              @Override
              public boolean isValid() {
                try {
                  SearchSetting ss = new SearchSetting();
                  ss.text = hcbSearchString.getText();
                  ss.caseSensitive = caseSensitive.isSelected();
                  ss.regexSearch = regexSearch.isSelected();
                  ss.mapCSSSearch = mapCSSSearch.isSelected();
                  SearchCompiler.compile(ss);
                  return true;
                } catch (ParseError | MapCSSException e) {
                  return false;
                }
              }
            });

    final JPanel p = new JPanel(new GridBagLayout());
    p.add(top, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 5, 5, 0));
    p.add(left, GBC.std().anchor(GBC.NORTH).insets(5, 10, 10, 0));
    p.add(right, GBC.eol());
    ExtendedDialog dialog =
        new ExtendedDialog(
            Main.parent,
            initialValues instanceof Filter ? tr("Filter") : tr("Search"),
            new String[] {
              initialValues instanceof Filter ? tr("Submit filter") : tr("Start Search"),
              tr("Cancel")
            }) {
          @Override
          protected void buttonAction(int buttonIndex, ActionEvent evt) {
            if (buttonIndex == 0) {
              try {
                SearchSetting ss = new SearchSetting();
                ss.text = hcbSearchString.getText();
                ss.caseSensitive = caseSensitive.isSelected();
                ss.regexSearch = regexSearch.isSelected();
                ss.mapCSSSearch = mapCSSSearch.isSelected();
                SearchCompiler.compile(ss);
                super.buttonAction(buttonIndex, evt);
              } catch (ParseError e) {
                Main.debug(e);
                JOptionPane.showMessageDialog(
                    Main.parent,
                    tr("Search expression is not valid: \n\n {0}", e.getMessage()),
                    tr("Invalid search expression"),
                    JOptionPane.ERROR_MESSAGE);
              }
            } else {
              super.buttonAction(buttonIndex, evt);
            }
          }
        };
    dialog.setButtonIcons(new String[] {"dialogs/search", "cancel"});
    dialog.configureContextsensitiveHelp("/Action/Search", true /* show help button */);
    dialog.setContent(p);
    dialog.showDialog();
    int result = dialog.getValue();

    if (result != 1) return null;

    // User pressed OK - let's perform the search
    SearchMode mode =
        replace.isSelected()
            ? SearchAction.SearchMode.replace
            : (add.isSelected()
                ? SearchAction.SearchMode.add
                : (remove.isSelected()
                    ? SearchAction.SearchMode.remove
                    : SearchAction.SearchMode.in_selection));
    initialValues.text = hcbSearchString.getText();
    initialValues.mode = mode;
    initialValues.caseSensitive = caseSensitive.isSelected();
    initialValues.allElements = allElements.isSelected();
    initialValues.regexSearch = regexSearch.isSelected();
    initialValues.mapCSSSearch = mapCSSSearch.isSelected();

    if (addOnToolbar.isSelected()) {
      ToolbarPreferences.ActionDefinition aDef =
          new ToolbarPreferences.ActionDefinition(Main.main.menu.search);
      aDef.getParameters().put(SEARCH_EXPRESSION, initialValues);
      // Display search expression as tooltip instead of generic one
      aDef.setName(Utils.shortenString(initialValues.text, MAX_LENGTH_SEARCH_EXPRESSION_DISPLAY));
      // parametrized action definition is now composed
      ActionParser actionParser = new ToolbarPreferences.ActionParser(null);
      String res = actionParser.saveAction(aDef);

      // add custom search button to toolbar preferences
      Main.toolbar.addCustomButton(res, -1, false);
    }
    return initialValues;
  }