From ccf2cdba8f06cbcf2d0beae2df82ca6c09abf369 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Mon, 12 Jan 2026 00:45:50 -0300 Subject: [PATCH 1/3] #2452: Add support for bookmark query highlighting --- .../src/main/java/iped/data/IBookmarks.java | 4 + .../main/java/iped/data/IMultiBookmarks.java | 4 + .../iped-desktop-messages.properties | 15 +- .../iped-desktop-messages_de_DE.properties | 17 +- .../iped-desktop-messages_es_AR.properties | 17 +- .../iped-desktop-messages_fr_FR.properties | 15 +- .../iped-desktop-messages_it_IT.properties | 15 +- .../iped-desktop-messages_pt_BR.properties | 15 +- iped-app/src/main/java/iped/app/ui/App.java | 5 + .../java/iped/app/ui/BookmarksManager.java | 151 ++++++++++++++--- .../iped/app/ui/BookmarksMismatchChecker.java | 69 ++++++++ .../iped/app/ui/BookmarksTreeListener.java | 153 +++++++++++++++++- .../app/ui/UICaseSearchFilterListener.java | 36 ++++- .../ui/bookmarks/BookmarkListRenderer.java | 14 +- .../bookmarks/BookmarkTreeCellRenderer.java | 12 +- .../ui/bookmarks/BookmarksMismatchDialog.java | 114 +++++++++++++ .../java/iped/app/ui/utils/AlertIcon.java | 44 +++++ .../iped/engine/data/BitmapBookmarks.java | 13 ++ .../main/java/iped/engine/data/Bookmarks.java | 12 ++ .../engine/data/MultiBitmapBookmarks.java | 14 ++ .../java/iped/engine/data/MultiBookmarks.java | 14 ++ 21 files changed, 695 insertions(+), 58 deletions(-) create mode 100644 iped-app/src/main/java/iped/app/ui/BookmarksMismatchChecker.java create mode 100644 iped-app/src/main/java/iped/app/ui/bookmarks/BookmarksMismatchDialog.java create mode 100644 iped-app/src/main/java/iped/app/ui/utils/AlertIcon.java diff --git a/iped-api/src/main/java/iped/data/IBookmarks.java b/iped-api/src/main/java/iped/data/IBookmarks.java index a38edb08f6..45f83db51e 100644 --- a/iped-api/src/main/java/iped/data/IBookmarks.java +++ b/iped-api/src/main/java/iped/data/IBookmarks.java @@ -93,6 +93,10 @@ public interface IBookmarks extends Serializable { String getBookmarkComment(int bookmarkId); + void setBookmarkQuery(int bookmarkId, String query); + + String getBookmarkQuery(int bookmarkId); + void setBookmarkColor(int bookmarkId, Color color); Color getBookmarkColor(int bookmarkId); diff --git a/iped-api/src/main/java/iped/data/IMultiBookmarks.java b/iped-api/src/main/java/iped/data/IMultiBookmarks.java index 8d368972c8..2d1817ddef 100644 --- a/iped-api/src/main/java/iped/data/IMultiBookmarks.java +++ b/iped-api/src/main/java/iped/data/IMultiBookmarks.java @@ -99,6 +99,10 @@ public interface IMultiBookmarks extends Serializable { void setBookmarkComment(String texto, String comment); + void setBookmarkQuery(String bookmarkName, String query); + + String getBookmarkQuery(String bookmarkName); + Color getBookmarkColor(String bookmarkName); void setBookmarkColor(String bookmarkName, Color color); diff --git a/iped-app/resources/localization/iped-desktop-messages.properties b/iped-app/resources/localization/iped-desktop-messages.properties index dfa6776be1..81f5db2b7e 100644 --- a/iped-app/resources/localization/iped-desktop-messages.properties +++ b/iped-app/resources/localization/iped-desktop-messages.properties @@ -51,7 +51,7 @@ BookmarksManager.AlertNoHighlightedItems=No highlighted items! BookmarksManager.AlertNoSelectedBookmarks=No selected bookmarks! BookmarksManager.Checked=Checked items BookmarksManager.ConfirmDelete=Really want to delete selected bookmarks? -BookmarksManager.ConfirmDelTitle=Confirm +BookmarksManager.ConfirmTitle=Confirm BookmarksManager.Dataset=Dataset: BookmarksManager.Delete=Delete BookmarksManager.Delete.Tip=Delete selected bookmarks @@ -66,16 +66,25 @@ BookmarksManager.KeyStrokeAlert1=Select just one bookmark to define key shortcut BookmarksManager.KeyStrokeAlert2=ALT is always used to remove from bookmark. BookmarksManager.KeyStrokeAlert3=Key shortcut already used, change it first. BookmarksManager.KeyStrokeAlert4=Key shortcut already used for other action. +BookmarksManager.Bookmark=Bookmark BookmarksManager.New=New BookmarksManager.New.Tip=New bookmark -BookmarksManager.NewBookmark.Tip=Enter bookmark name +BookmarksManager.NewBookmark.Tip=Bookmark name BookmarksManager.Remove=Remove BookmarksManager.Remove.Tip=Remove items from selected bookmarks BookmarksManager.SearchingDuplicates=Searching Duplicates... BookmarksManager.Title=Bookmarks BookmarksManager.CommentsTooltip=Bookmark comments +BookmarksManager.CommentsTooltip.Multicase=Bookmark comments (as stored in the first associated case) +BookmarksManager.Mismatch=Mismatch +BookmarksManager.Mismatch.Title=Bookmark query mismatch +BookmarksManager.MismatchButtonTooltip=Query mismatch detected!
Click to view query per case +BookmarksManager.MismatchTooltip=Bookmark query differs between cases; highlighting is disabled to avoid conflicting results.
Consider updating the bookmark to unify the queries across cases. BookmarksManager.Update=Update -BookmarksManager.UpdateTooltip=Update comments +BookmarksManager.UpdateTooltip=Update comments and/or query +BookmarksManager.ConfirmUpdate=Updates will be applied to associated cases.\nDo you want to proceed? +BookmarksManager.QueryTooltip=Bookmark query +BookmarksManager.QueryTooltip.Multicase=Bookmark query (as stored in the first associated case) BookmarksManager.AlreadyExists=Bookmark name already exists! BookmarksTreeModel.NoBookmarks=[No Bookmarks] BookmarksTreeModel.RootName=Bookmarks diff --git a/iped-app/resources/localization/iped-desktop-messages_de_DE.properties b/iped-app/resources/localization/iped-desktop-messages_de_DE.properties index 7edec9279d..91b9ed9e9b 100644 --- a/iped-app/resources/localization/iped-desktop-messages_de_DE.properties +++ b/iped-app/resources/localization/iped-desktop-messages_de_DE.properties @@ -51,7 +51,7 @@ BookmarksManager.AlertNoHighlightedItems=Keine hervorgehobenen Elemente BookmarksManager.AlertNoSelectedBookmarks=keine ausgewählten Lesezeichen BookmarksManager.Checked=ausgewählte Objekte BookmarksManager.ConfirmDelete=Ausgewählte Lesezeichen wirklich löschen? -BookmarksManager.ConfirmDelTitle=Bestätigung +BookmarksManager.ConfirmTitle=Bestätigung BookmarksManager.Dataset=Datensatz: BookmarksManager.Delete=Löschen BookmarksManager.Delete.Tip=lösche markierte Lesezeichen @@ -66,16 +66,25 @@ BookmarksManager.KeyStrokeAlert1=Bitte ein Lesezeichen wählen um einen Kurzbefe BookmarksManager.KeyStrokeAlert2=ALT ist reserviert zum Entfernen vom Lesezeichen. BookmarksManager.KeyStrokeAlert3=Kurzbefehl ist bereits in Verwendung, bitte ändern. BookmarksManager.KeyStrokeAlert4=Kurzbefehl wird bereits für eine andere Aktion verwendet. +BookmarksManager.Bookmark=Lesezeichen BookmarksManager.New=Neu BookmarksManager.New.Tip=Neues Lesezeichen -BookmarksManager.NewBookmark.Tip=Name für Lesezeichen eingeben +BookmarksManager.NewBookmark.Tip=Name für Lesezeichen BookmarksManager.Remove=Entfernen BookmarksManager.Remove.Tip=Entferne Objekte von ausgewähltem Lesezeichen BookmarksManager.SearchingDuplicates=Suche Duplikate... BookmarksManager.Title=Lesezeichen BookmarksManager.CommentsTooltip=Kommentare zu Lesezeichen -BookmarksManager.Update=Update -BookmarksManager.UpdateTooltip=Kommentare zu Updates +BookmarksManager.CommentsTooltip.Multicase==Kommentare zu Lesezeichen (wie im ersten zugeordneten Fall gespeichert) +BookmarksManager.Mismatch=Abweichung +BookmarksManager.Mismatch.Title=Lesezeichen-Abfrage-Abweichung +BookmarksManager.MismatchButtonTooltip=Abfrage-Abweichung erkannt!
Klicken, um die Abfrage pro Fall anzuzeigen +BookmarksManager.MismatchTooltip=Die Lesezeichen-Abfrage unterscheidet sich zwischen den Fällen; das Hervorheben ist deaktiviert, um widersprüchliche Ergebnisse zu vermeiden.
Erwägen Sie das Aktualisieren des Lesezeichens, um die Abfragen über alle Fälle hinweg einheitlich zu machen. +BookmarksManager.Update=Aktualisieren +BookmarksManager.UpdateTooltip=Kommentare und/oder Abfrage aktualisieren +BookmarksManager.ConfirmUpdate=Aktualisierungen werden auf die verknüpften Fälle angewendet.\nMöchten Sie fortfahren? +BookmarksManager.QueryTooltip=Lesezeichen-Abfrage +BookmarksManager.QueryTooltip.Multicase=Lesezeichen-Abfrage (wie im ersten zugeordneten Fall gespeichert) BookmarksManager.AlreadyExists=Lesezeichen ist bereits vorhanden! BookmarksTreeModel.NoBookmarks=[Keine Lesezeichen] BookmarksTreeModel.RootName=Lesezeichen diff --git a/iped-app/resources/localization/iped-desktop-messages_es_AR.properties b/iped-app/resources/localization/iped-desktop-messages_es_AR.properties index 49cdd687a0..41b9ba2b57 100644 --- a/iped-app/resources/localization/iped-desktop-messages_es_AR.properties +++ b/iped-app/resources/localization/iped-desktop-messages_es_AR.properties @@ -51,7 +51,7 @@ BookmarksManager.AlertNoHighlightedItems=NO hay elementos resaltados! BookmarksManager.AlertNoSelectedBookmarks=No hay marcadores seleccionados! BookmarksManager.Checked=Elementos marcados BookmarksManager.ConfirmDelete=¿Realmente desea eliminar los marcadores seleccionados? -BookmarksManager.ConfirmDelTitle=Confirmar +BookmarksManager.ConfirmTitle=Confirmar BookmarksManager.Dataset=Conjunto de datos: BookmarksManager.Delete=Eliminar BookmarksManager.Delete.Tip=Eliminar marcadores seleccionados @@ -66,16 +66,25 @@ BookmarksManager.KeyStrokeAlert1=Seleccione solo 1 marcador para definir el ataj BookmarksManager.KeyStrokeAlert2=Alt siempre se usa para eliminar de los marcadores. BookmarksManager.KeyStrokeAlert3=Atajo de teclado ya utilizado, cámbielo primero. BookmarksManager.KeyStrokeAlert4=Tecla de método abreviado ya utilizada para otra acción. +BookmarksManager.Bookmark=Marcador BookmarksManager.New=Nuevo BookmarksManager.New.Tip=Nuevo marcador -BookmarksManager.NewBookmark.Tip=Ingrese el nombre del marcador +BookmarksManager.NewBookmark.Tip=Nombre del marcador BookmarksManager.Remove=Eliminar BookmarksManager.Remove.Tip=Eliminar elementos de los marcadores seleccionados BookmarksManager.SearchingDuplicates=Buscando duplicados... BookmarksManager.Title=Marcadores -BookmarksManager.CommentsTooltip=Marcar comentarios +BookmarksManager.CommentsTooltip=Comentarios del marcador +BookmarksManager.CommentsTooltip.Multicase=Comentarios del marcador (como se almacenan en el primer caso asociado) +BookmarksManager.Mismatch=Incoherencia +BookmarksManager.Mismatch.Title=Incoherencia de consulta del marcador +BookmarksManager.MismatchButtonTooltip=Incoherencia de consulta detectada!
Haga clic para ver la consulta por caso +BookmarksManager.MismatchTooltip=La consulta del marcador difiere entre los casos; el resaltado está deshabilitado para evitar resultados contradictorios.
Considere actualizar el marcador para unificar las consultas entre los casos. BookmarksManager.Update=Actualizar -BookmarksManager.UpdateTooltip=Actualizar comentarios +BookmarksManager.UpdateTooltip=Actualizar comentarios y/o consulta +BookmarksManager.ConfirmUpdate=Las actualizaciones se aplicarán a los casos asociados.\n¿Desea continuar? +BookmarksManager.QueryTooltip=Consulta asociada al marcador +BookmarksManager.QueryTooltip.Multicase=Consulta asociada al marcador (como se almacena en el primer caso asociado) BookmarksManager.AlreadyExists=¡El nombre del marcador ya existe! BookmarksTreeModel.NoBookmarks=[Sin marcadores] BookmarksTreeModel.RootName=Marcadores diff --git a/iped-app/resources/localization/iped-desktop-messages_fr_FR.properties b/iped-app/resources/localization/iped-desktop-messages_fr_FR.properties index b6a6e6d11d..bd0b009f93 100644 --- a/iped-app/resources/localization/iped-desktop-messages_fr_FR.properties +++ b/iped-app/resources/localization/iped-desktop-messages_fr_FR.properties @@ -51,7 +51,7 @@ BookmarksManager.AlertNoHighlightedItems=Aucun élément surligné \! BookmarksManager.AlertNoSelectedBookmarks=Aucun favori sélectionné \! BookmarksManager.Checked=Éléments cochés BookmarksManager.ConfirmDelete=Voulez-vous vraiment supprimer les favoris sélectionnés ? -BookmarksManager.ConfirmDelTitle=Confirmer +BookmarksManager.ConfirmTitle=Confirmer BookmarksManager.Dataset=Données : BookmarksManager.Delete=Supprimer BookmarksManager.Delete.Tip=Supprimer les favoris sélectionnés @@ -66,16 +66,25 @@ BookmarksManager.KeyStrokeAlert1=Créer un raccourci pour sélectionner un favor BookmarksManager.KeyStrokeAlert2=Maintenir Alt pour effacer une favori. BookmarksManager.KeyStrokeAlert3=Modifier le raccourci. BookmarksManager.KeyStrokeAlert4=Maintenir la touche de raccourci pour une autre action. +BookmarksManager.Bookmark=Favori BookmarksManager.New=Nouveau BookmarksManager.New.Tip=Nouveau favori -BookmarksManager.NewBookmark.Tip=Saisissez le nom du favori +BookmarksManager.NewBookmark.Tip=Nom du favori BookmarksManager.Remove=Supprimer BookmarksManager.Remove.Tip=Supprimer le favori sélectionné BookmarksManager.SearchingDuplicates=Recherche des doublons... BookmarksManager.Title=Favoris BookmarksManager.CommentsTooltip=Commentaires du favori +BookmarksManager.CommentsTooltip.Multicase=Commentaires du favori (comme stockés dans le premier cas associé) +BookmarksManager.Mismatch=Incohérence +BookmarksManager.Mismatch.Title=Incohérence de la requête du favori +BookmarksManager.MismatchButtonTooltip=Incohérence de la requête détectée!
Cliquez pour afficher la requête par cas +BookmarksManager.MismatchTooltip=La requête du favori diffère entre les cas; le surlignage est désactivé pour éviter des résultats contradictoires.
Considérez la mise à jour du favori pour unifier les requêtes entre les cas. BookmarksManager.Update=Mise à jour -BookmarksManager.UpdateTooltip=Mettre à jour les commentaires +BookmarksManager.UpdateTooltip=Mettre à jour les commentaires et/ou la requête +BookmarksManager.ConfirmUpdate=Les mises à jour seront appliquées aux cas associés.\nVoulez-vous continuer ? +BookmarksManager.QueryTooltip=Requête associée au favori +BookmarksManager.QueryTooltip.Multicase=Requête associée au favori (comme stockée dans le premier cas associé) BookmarksManager.AlreadyExists=Nom du favori existe déjà \! BookmarksTreeModel.NoBookmarks=[Aucun favori] BookmarksTreeModel.RootName=Favoris diff --git a/iped-app/resources/localization/iped-desktop-messages_it_IT.properties b/iped-app/resources/localization/iped-desktop-messages_it_IT.properties index 03b2c34311..753a108a44 100644 --- a/iped-app/resources/localization/iped-desktop-messages_it_IT.properties +++ b/iped-app/resources/localization/iped-desktop-messages_it_IT.properties @@ -51,7 +51,7 @@ BookmarksManager.AlertNoHighlightedItems=Nessun elemento evidenziato! BookmarksManager.AlertNoSelectedBookmarks=Nessun segnalibro selezionato! BookmarksManager.Checked=Elementi selezionati BookmarksManager.ConfirmDelete=Vuoi davvero eliminare i segnalibri selezionati? -BookmarksManager.ConfirmDelTitle=Conferma +BookmarksManager.ConfirmTitle=Conferma BookmarksManager.Dataset=Set di dati: BookmarksManager.Delete=Elimina BookmarksManager.Delete.Tip=Elimina i segnalibri selezionati @@ -66,16 +66,25 @@ BookmarksManager.KeyStrokeAlert1=Seleziona solo un segnalibro per definire la sc BookmarksManager.KeyStrokeAlert2=ALT è sempre usato per rimuovere l''elemento dal segnalibro. BookmarksManager.KeyStrokeAlert3=Scorciatoia da tastiera già utilizzata, cambiala prima. BookmarksManager.KeyStrokeAlert4=Scorciatoia da tastiera già utilizzata per un''altra azione. +BookmarksManager.Bookmark=Segnalibro BookmarksManager.New=Nuovo BookmarksManager.New.Tip=Nuovo segnalibro -BookmarksManager.NewBookmark.Tip=Inserisci il nome del segnalibro +BookmarksManager.NewBookmark.Tip=Nome del segnalibro BookmarksManager.Remove=Rimuovi BookmarksManager.Remove.Tip=Rimuovi elementi dai segnalibri selezionati BookmarksManager.SearchingDuplicates=Cercando duplicati... BookmarksManager.Title=Segnalibri BookmarksManager.CommentsTooltip=Commenti ai segnalibri +BookmarksManager.CommentsTooltip.Multicase=Commenti ai segnalibri (come memorizzati nel primo caso associato) +BookmarksManager.Mismatch=Inconsistenza +BookmarksManager.Mismatch.Title=Inconsistenza nella query del segnalibro +BookmarksManager.MismatchButtonTooltip=Query inconsistente rilevata!
Clicca per visualizzare la query per caso +BookmarksManager.MismatchTooltip=La query del segnalibro differisce tra i casi; l'evidenziazione è disabilitata per evitare risultati contrastanti.
Valuta di aggiornare il segnalibro per uniformare le query tra i casi. BookmarksManager.Update=Aggiorna -BookmarksManager.UpdateTooltip=Aggiorna commento +BookmarksManager.UpdateTooltip=Aggiorna commenti e/o query +BookmarksManager.ConfirmUpdate=Aggiornamenti saranno applicati ai casi associati.\nVuoi continuare? +BookmarksManager.QueryTooltip=Query associata al segnalibro +BookmarksManager.QueryTooltip.Multicase=Query associata al segnalibro (come memorizzata nel primo caso associato) BookmarksManager.AlreadyExists=Il nome del segnalibro esiste già! BookmarksTreeModel.NoBookmarks=[Nessun segnalibro] BookmarksTreeModel.RootName=Segnalibri diff --git a/iped-app/resources/localization/iped-desktop-messages_pt_BR.properties b/iped-app/resources/localization/iped-desktop-messages_pt_BR.properties index 6eb2c25725..8c2d9a3bfc 100644 --- a/iped-app/resources/localization/iped-desktop-messages_pt_BR.properties +++ b/iped-app/resources/localization/iped-desktop-messages_pt_BR.properties @@ -51,7 +51,7 @@ BookmarksManager.AlertNoHighlightedItems=Nenhum item destacado! BookmarksManager.AlertNoSelectedBookmarks=Nenhum marcador selecionado! BookmarksManager.Checked=Itens Marcados BookmarksManager.ConfirmDelete=Deseja realmente apagar os marcadores selecionados? -BookmarksManager.ConfirmDelTitle=Confirmar +BookmarksManager.ConfirmTitle=Confirmar BookmarksManager.Dataset=Conjunto de dados: BookmarksManager.Delete=Apagar BookmarksManager.Delete.Tip=Apagar marcadores selecionados @@ -66,16 +66,25 @@ BookmarksManager.KeyStrokeAlert1=Selecione apenas um marcador para definir a tec BookmarksManager.KeyStrokeAlert2=Tecla ALT é sempre usada para remover do marcador. BookmarksManager.KeyStrokeAlert3=Tecla de atalho já utilizada, substitua ela primeiro. BookmarksManager.KeyStrokeAlert4=Tecla de atalho já utilizada para outra ação. +BookmarksManager.Bookmark=Marcador BookmarksManager.New=Criar novo BookmarksManager.New.Tip=Novo marcador -BookmarksManager.NewBookmark.Tip=Novo marcador +BookmarksManager.NewBookmark.Tip=Nome do marcador BookmarksManager.Remove=Remover BookmarksManager.Remove.Tip=Remover itens dos marcadores selecionados BookmarksManager.SearchingDuplicates=Pesquisando duplicatas... BookmarksManager.Title=Marcadores BookmarksManager.CommentsTooltip=Comentários do marcador +BookmarksManager.CommentsTooltip.Multicase=Comentários do marcador (conforme armazenado no primeiro caso associado) +BookmarksManager.Mismatch=Inconsistência +BookmarksManager.Mismatch.Title=Inconsistência na consulta do marcador +BookmarksManager.MismatchButtonTooltip=Consulta inconsistente detectada!
Clique para visualizar consulta por caso +BookmarksManager.MismatchTooltip=Consulta do marcador diverge entre os casos; o realce está desativado para evitar resultados conflitantes.
Considere atualizar o marcador para unificar as consultas entre os casos. BookmarksManager.Update=Atualizar -BookmarksManager.UpdateTooltip=Atualizar comentários +BookmarksManager.UpdateTooltip=Atualizar comentários e/ou consulta +BookmarksManager.ConfirmUpdate=Atualizações serão aplicadas aos casos associados.\nDeseja continuar? +BookmarksManager.QueryTooltip=Consulta associada ao marcador +BookmarksManager.QueryTooltip.Multicase=Consulta associada ao marcador (conforme armazenado no primeiro caso associado) BookmarksManager.AlreadyExists=Nome de marcador já existente! BookmarksTreeModel.NoBookmarks=[Sem Marcadores] BookmarksTreeModel.RootName=Marcadores diff --git a/iped-app/src/main/java/iped/app/ui/App.java b/iped-app/src/main/java/iped/app/ui/App.java index 3e0691abe9..3fb3b46357 100644 --- a/iped-app/src/main/java/iped/app/ui/App.java +++ b/iped-app/src/main/java/iped/app/ui/App.java @@ -285,6 +285,7 @@ public class App extends JFrame implements WindowListener, IMultiSearchResultPro final static String FILTRO_TODOS = Messages.getString("App.NoFilter"); //$NON-NLS-1$ final static String FILTRO_SELECTED = Messages.getString("App.Checked"); //$NON-NLS-1$ public final static String SEARCH_TOOL_TIP = Messages.getString("App.SearchBoxTip"); //$NON-NLS-1$ + public final static String ACTIVE_BOOKMARK_QUERY_PROPERTY = "active_bookmark_query"; //$NON-NLS-1$ static int MAX_HITS = 10000; @@ -330,6 +331,10 @@ public static final App get() { return app; } + public boolean isMultiCase() { + return this.isMultiCase; + } + public void setLastSelectedDoc(int lastSelectedDoc) { this.lastSelectedDoc = lastSelectedDoc; } diff --git a/iped-app/src/main/java/iped/app/ui/BookmarksManager.java b/iped-app/src/main/java/iped/app/ui/BookmarksManager.java index 8eb83f83be..1249065bc9 100644 --- a/iped-app/src/main/java/iped/app/ui/BookmarksManager.java +++ b/iped-app/src/main/java/iped/app/ui/BookmarksManager.java @@ -68,6 +68,7 @@ import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.TermQuery; import org.apache.lucene.util.BytesRef; +import org.checkerframework.checker.units.qual.A; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -75,6 +76,7 @@ import iped.app.ui.bookmarks.BookmarkColorsUtil; import iped.app.ui.bookmarks.BookmarkEditDialog; import iped.app.ui.bookmarks.BookmarkListRenderer; +import iped.app.ui.bookmarks.BookmarksMismatchDialog; import iped.app.ui.utils.JTextFieldLimited; import iped.data.IItem; import iped.data.IItemId; @@ -108,10 +110,12 @@ public class BookmarksManager implements ActionListener, ListSelectionListener, JButton butRemove = new JButton(Messages.getString("BookmarksManager.Remove")); //$NON-NLS-1$ JButton butEdit = new JButton(Messages.getString("BookmarksManager.Edit")); //$NON-NLS-1$ JTextFieldLimited newBookmark = new JTextFieldLimited(); - JTextArea comments = new JTextArea(); + JTextArea commentsTextArea = new JTextArea(); + JTextArea queryTextArea = new JTextArea(); JButton butNew = new JButton(Messages.getString("BookmarksManager.New")); //$NON-NLS-1$ - JButton butUpdateComment = new JButton(Messages.getString("BookmarksManager.Update")); //$NON-NLS-1$ + JButton butUpdate = new JButton(Messages.getString("BookmarksManager.Update")); //$NON-NLS-1$ JButton butDelete = new JButton(Messages.getString("BookmarksManager.Delete")); //$NON-NLS-1$ + JButton butMismatch = new JButton(Messages.getString("BookmarksManager.Mismatch")); DefaultListModel listModel = new DefaultListModel<>(); JList list = new JList<>(listModel); JScrollPane scrollList = new JScrollPane(list); @@ -130,7 +134,8 @@ public static void setVisible() { instance.dialog.setVisible(true); instance.list.clearSelection(); instance.newBookmark.setText(""); - instance.comments.setText(""); + instance.commentsTextArea.setText(""); + instance.queryTextArea.setText(""); } public static void updateCounters() { @@ -155,13 +160,24 @@ private BookmarksManager() { newBookmark.setLimit(maxBookmarkNameLength); newBookmark.setToolTipText(Messages.getString("BookmarksManager.NewBookmark.Tip")); //$NON-NLS-1$ - comments.setToolTipText(Messages.getString("BookmarksManager.CommentsTooltip")); //$NON-NLS-1$ - butUpdateComment.setToolTipText(Messages.getString("BookmarksManager.UpdateTooltip")); //$NON-NLS-1$ + if (App.get().isMultiCase()) { + commentsTextArea.setToolTipText(Messages.getString("BookmarksManager.CommentsTooltip.Multicase")); //$NON-NLS-1$ + queryTextArea.setToolTipText(Messages.getString("BookmarksManager.QueryTooltip.Multicase")); //$NON-NLS-1$ + } + else { + commentsTextArea.setToolTipText(Messages.getString("BookmarksManager.CommentsTooltip")); //$NON-NLS-1$ + queryTextArea.setToolTipText(Messages.getString("BookmarksManager.QueryTooltip")); //$NON-NLS-1$ + } + butUpdate.setToolTipText(Messages.getString("BookmarksManager.UpdateTooltip")); //$NON-NLS-1$ butNew.setToolTipText(Messages.getString("BookmarksManager.New.Tip")); //$NON-NLS-1$ butAdd.setToolTipText(Messages.getString("BookmarksManager.Add.Tip")); //$NON-NLS-1$ butRemove.setToolTipText(Messages.getString("BookmarksManager.Remove.Tip")); //$NON-NLS-1$ butDelete.setToolTipText(Messages.getString("BookmarksManager.Delete.Tip")); //$NON-NLS-1$ butEdit.setToolTipText(Messages.getString("BookmarksManager.Edit.Tip")); //$NON-NLS-1$ + butMismatch.setToolTipText(Messages.getString("BookmarksManager.MismatchButtonTooltip")); //$NON-NLS-1$ + + butMismatch.setForeground(Color.RED); + butMismatch.setVisible(false); JPanel top = new JPanel(new GridLayout(3, 2, 0, 5)); top.add(msg); @@ -177,7 +193,8 @@ private BookmarksManager() { left1.setLayout(new BoxLayout(left1, BoxLayout.PAGE_AXIS)); JPanel left0 = new JPanel(new GridLayout(0, 1, 0, 0)); left0.add(butNew); - left0.add(butUpdateComment); + left0.add(butUpdate); + left0.add(butMismatch); left1.add(left0); left1.add(Box.createVerticalStrut(65)); JPanel left3 = new JPanel(new GridLayout(0, 1, 0, 0)); @@ -193,18 +210,26 @@ private BookmarksManager() { left.add(left1, BorderLayout.PAGE_START); left.add(left2, BorderLayout.PAGE_END); - comments.setLineWrap(true); - comments.setWrapStyleWord(true); - JScrollPane commentScroll = new JScrollPane(comments); + commentsTextArea.setLineWrap(true); + commentsTextArea.setWrapStyleWord(true); + JScrollPane commentScroll = new JScrollPane(commentsTextArea); commentScroll.setPreferredSize(new Dimension(300, 50)); + queryTextArea.setLineWrap(true); + queryTextArea.setWrapStyleWord(true); + JScrollPane queryScroll = new JScrollPane(queryTextArea); + queryScroll.setPreferredSize(new Dimension(300, 50)); + JLabel shortcutBookmark = new JLabel(Messages.getString("BookmarksManager.KeyStrokeBookmark")); shortcutBookmark.setBorder(BorderFactory.createEmptyBorder(6, 1, 0, 0)); JPanel center = new JPanel(new BorderLayout()); JPanel bookmark = new JPanel(new BorderLayout()); bookmark.add(newBookmark, BorderLayout.PAGE_START); - bookmark.add(commentScroll, BorderLayout.CENTER); + Box verticalBox = Box.createVerticalBox(); + verticalBox.add(commentScroll); + verticalBox.add(queryScroll); + bookmark.add(verticalBox, BorderLayout.CENTER); bookmark.add(shortcutBookmark, BorderLayout.PAGE_END); center.add(bookmark, BorderLayout.PAGE_START); center.add(scrollList, BorderLayout.CENTER); @@ -217,11 +242,12 @@ private BookmarksManager() { dialog.getContentPane().add(pane); butAdd.addActionListener(this); - butUpdateComment.addActionListener(this); + butUpdate.addActionListener(this); butRemove.addActionListener(this); butEdit.addActionListener(this); butNew.addActionListener(this); butDelete.addActionListener(this); + butMismatch.addActionListener(this); list.addListSelectionListener(this); // disable selection by typing @@ -241,6 +267,7 @@ public void mouseClicked(MouseEvent e) { } }); list.setCellRenderer(new BookmarkListRenderer()); + list.setFixedCellHeight(19); dialog.setLocationRelativeTo(App.get()); @@ -425,7 +452,7 @@ private void includeDuplicates(ArrayList uniqueSelectedIds) { @Override public void actionPerformed(final ActionEvent evt) { - if (evt.getSource() == butAdd || evt.getSource() == butRemove || evt.getSource() == butUpdateComment || evt.getSource() == butEdit || evt.getSource() == butDelete) { + if (evt.getSource() == butAdd || evt.getSource() == butRemove || evt.getSource() == butUpdate || evt.getSource() == butEdit || evt.getSource() == butDelete) { // Check if there is at least one bookmark selected if (list.getSelectedIndex() == -1) { showMessage(Messages.getString("BookmarksManager.AlertNoSelectedBookmarks")); @@ -433,7 +460,7 @@ public void actionPerformed(final ActionEvent evt) { } } - if (evt.getSource() == butUpdateComment || evt.getSource() == butEdit) { + if (evt.getSource() == butUpdate || evt.getSource() == butEdit) { // Check if there is more than one bookmark selected if (list.getSelectedIndices().length > 1) { showMessage(Messages.getString("BookmarksManager.AlertMultipleSelectedBookmarks")); @@ -444,11 +471,14 @@ public void actionPerformed(final ActionEvent evt) { IMultiBookmarks multiBookmarks = App.get().appCase.getMultiBookmarks(); if (evt.getSource() == butNew) { String name = newBookmark.getText().trim(); - String comment = comments.getText().trim(); + String comment = commentsTextArea.getText().trim(); + String query = queryTextArea.getText().trim(); if (!name.isEmpty() && !listModel.contains(new BookmarkAndKey(name))) { multiBookmarks.newBookmark(name); multiBookmarks.setBookmarkComment(name, comment); + multiBookmarks.setBookmarkQuery(name, query); multiBookmarks.setBookmarkColor(name, BookmarkColorsUtil.getInitialColor(multiBookmarks.getUsedColors(), name)); + BookmarksMismatchChecker.get().setMismatch(name, false); updateList(); } list.clearSelection(); @@ -457,13 +487,48 @@ public void actionPerformed(final ActionEvent evt) { list.setSelectedIndex(i); } } - } - if (evt.getSource() == butUpdateComment) { + + if (evt.getSource() == butUpdate) { + // Show alert if in multicase mode + if (App.get().isMultiCase()) { + int choice = JOptionPane.showConfirmDialog(dialog, Messages.getString("BookmarksManager.ConfirmUpdate"), + Messages.getString("BookmarksManager.ConfirmTitle"), JOptionPane.YES_NO_OPTION); + if (choice == JOptionPane.NO_OPTION) { + return; + } + } int idx = list.getSelectedIndex(); String bookmarkName = list.getModel().getElementAt(idx).getName(); - multiBookmarks.setBookmarkComment(bookmarkName, comments.getText()); + String newQuery = queryTextArea.getText().trim(); + + multiBookmarks.setBookmarkComment(bookmarkName, commentsTextArea.getText().trim()); + multiBookmarks.setBookmarkQuery(bookmarkName, newQuery); multiBookmarks.saveState(); + BookmarksMismatchChecker.get().setMismatch(bookmarkName, false); + queryTextArea.setForeground(Color.BLACK); + + // Hide "Mismatch" button + butMismatch.setVisible(false); + butMismatch.getParent().revalidate(); + butMismatch.getParent().repaint(); + + // Repaint the list to update the icon (alert icon is removed if there was a mismatch) + list.repaint(); + + BookmarksController.get().updateUI(); + + // Check if the updated bookmark is the only one selected in the bookmark tree + // If so, trigger a refresh to apply the new query highlighting + Set selectedBookmarks = App.get().bookmarksListener.getSelectedBookmarkNames(); + if (selectedBookmarks.size() == 1 && selectedBookmarks.contains(bookmarkName)) { + // Update the client property with the new query + String normalizedQuery = (newQuery != null && !newQuery.isEmpty()) ? newQuery : null; + App.get().queryComboBox.putClientProperty(App.ACTIVE_BOOKMARK_QUERY_PROPERTY, normalizedQuery); + + // Trigger file listing update to refresh with new bookmark query + App.get().appletListener.updateFileListing(); + } } if (evt.getSource() == butAdd || evt.getSource() == butRemove || evt.getSource() == butNew) { @@ -479,16 +544,16 @@ public void actionPerformed(final ActionEvent evt) { } else if (evt.getSource() == butDelete) { int result = JOptionPane.showConfirmDialog(dialog, Messages.getString("BookmarksManager.ConfirmDelete"), //$NON-NLS-1$ - Messages.getString("BookmarksManager.ConfirmDelTitle"), JOptionPane.YES_NO_OPTION); //$NON-NLS-1$ + Messages.getString("BookmarksManager.ConfirmTitle"), JOptionPane.YES_NO_OPTION); //$NON-NLS-1$ if (result == JOptionPane.YES_OPTION) { for (int index : list.getSelectedIndices()) { String bookmark = list.getModel().getElementAt(index).getName(); multiBookmarks.delBookmark(bookmark); + BookmarksMismatchChecker.get().setMismatch(bookmark, null); } updateList(); multiBookmarks.saveState(); BookmarksController.get().updateUI(); - } } else if (evt.getSource() == butEdit) { @@ -520,11 +585,18 @@ public void actionPerformed(final ActionEvent evt) { if (changed) { multiBookmarks.saveState(); + boolean queryMismatch = BookmarksMismatchChecker.get().queryMismatch(currentName); + BookmarksMismatchChecker.get().setMismatch(currentName, null); + BookmarksMismatchChecker.get().setMismatch(newName, queryMismatch); BookmarksController.get().updateUI(); list.repaint(); } + + } else if (evt.getSource() == butMismatch) { + String currentName = list.getSelectedValue().getName(); + BookmarksMismatchDialog mismatchDialog = new BookmarksMismatchDialog(dialog, currentName, App.get().appCase.getAtomicSources()); + mismatchDialog.setVisible(true); } - } private ArrayList getUniqueSelectedIds() { @@ -584,16 +656,45 @@ public void run() { @Override public void valueChanged(ListSelectionEvent e) { - int idx = list.getSelectedIndex(); - if (idx == -1) { - comments.setText(null); + // Do nothing if selection has not finished changing + if (e.getValueIsAdjusting()) + return; + + // Behaviour if more than one bookmark is selected + int[] selectedIndices = list.getSelectedIndices(); + if (selectedIndices.length != 1) { newBookmark.setText(null); + commentsTextArea.setText(null); + queryTextArea.setText(null); + if (App.get().isMultiCase()) { + butMismatch.setVisible(false); + butMismatch.getParent().revalidate(); + butMismatch.getParent().repaint(); + } return; } + + int idx = selectedIndices[0]; String bookmarkName = list.getModel().getElementAt(idx).getName(); - String comment = App.get().appCase.getMultiBookmarks().getBookmarkComment(bookmarkName); + String comments = App.get().appCase.getMultiBookmarks().getBookmarkComment(bookmarkName); + String query = App.get().appCase.getMultiBookmarks().getBookmarkQuery(bookmarkName); + newBookmark.setText(bookmarkName); - comments.setText(comment); + commentsTextArea.setText(comments); + commentsTextArea.setCaretPosition(0); + queryTextArea.setText(query); + queryTextArea.setCaretPosition(0); + boolean queryMismatch = BookmarksMismatchChecker.get().queryMismatch(bookmarkName); + if (queryMismatch) { + queryTextArea.setForeground(Color.RED); + } else { + queryTextArea.setForeground(Color.BLACK); + } + + // Handle "Mismatch" button visibility + butMismatch.setVisible(queryMismatch); + butMismatch.getParent().revalidate(); + butMismatch.getParent().repaint(); } @Override diff --git a/iped-app/src/main/java/iped/app/ui/BookmarksMismatchChecker.java b/iped-app/src/main/java/iped/app/ui/BookmarksMismatchChecker.java new file mode 100644 index 0000000000..b821f7aefd --- /dev/null +++ b/iped-app/src/main/java/iped/app/ui/BookmarksMismatchChecker.java @@ -0,0 +1,69 @@ +package iped.app.ui; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import iped.data.IBookmarks; +import iped.data.IIPEDSource; + +/** + * Singleton class to check and cache bookmark query mismatches across multiple cases. + */ +public class BookmarksMismatchChecker { + + private static BookmarksMismatchChecker instance = new BookmarksMismatchChecker(); + + private final Map mismatchMap = new HashMap<>(); + + public static BookmarksMismatchChecker get() { + return instance; + } + + public void setMismatch(String bookmarkName, Boolean isMismatch) { + if (isMismatch == null) { + mismatchMap.remove(bookmarkName); + } + else { + mismatchMap.put(bookmarkName, isMismatch.booleanValue()); + } + } + + public boolean queryMismatch(String bookmarkName) { + if (!App.get().isMultiCase()) { + return false; // Single case: no mismatch + } + + // Multi-case: check the cached value or compute it + if (mismatchMap.containsKey(bookmarkName)) { + return mismatchMap.get(bookmarkName); + } + return mismatchMap.computeIfAbsent(bookmarkName, k -> queryDiffers(k)); + } + + private boolean queryDiffers(String bookmarkName) { + String firstQuery = null; + boolean firstSet = false; + + for (IIPEDSource source : App.get().appCase.getAtomicSources()) { + IBookmarks bookmarks = source.getBookmarks(); + + // Get the bookmark ID for the bookmark in the case + int bookmarkId = bookmarks.getBookmarkId(bookmarkName); + if (bookmarkId == -1) { + continue; // the bookmark doesn't exist in the case + } + + if (!firstSet) { + firstQuery = bookmarks.getBookmarkQuery(bookmarkId); + firstSet = true; + } else { + if (!Objects.equals(firstQuery, bookmarks.getBookmarkQuery(bookmarkId))) { + return true; // bookmark query differs between cases + } + } + } + + return false; // bookmark query is the same across all cases + } +} diff --git a/iped-app/src/main/java/iped/app/ui/BookmarksTreeListener.java b/iped-app/src/main/java/iped/app/ui/BookmarksTreeListener.java index d4fb0fe1f5..d797468066 100644 --- a/iped-app/src/main/java/iped/app/ui/BookmarksTreeListener.java +++ b/iped-app/src/main/java/iped/app/ui/BookmarksTreeListener.java @@ -14,8 +14,11 @@ import javax.swing.event.TreeSelectionListener; import javax.swing.tree.TreePath; +import org.apache.commons.lang3.ArrayUtils; import org.roaringbitmap.RoaringBitmap; +import iped.data.IBookmarks; +import iped.data.IIPEDSource; import iped.data.IMultiBookmarks; import iped.engine.data.MultiBitmapBookmarks; import iped.engine.search.MultiSearchResult; @@ -76,6 +79,20 @@ public void valueChanged(TreeSelectionEvent evt) { } } + // Store bookmark query for the filter to use + if (selection.size() == 1) { + Object selected = selection.iterator().next(); + if (selected != BookmarksTreeModel.ROOT && selected != BookmarksTreeModel.NO_BOOKMARKS) { + String bookmarkName = selected.toString(); + String unifiedQuery = getUnifiedBookmarkQuery(bookmarkName); + App.get().queryComboBox.putClientProperty(App.ACTIVE_BOOKMARK_QUERY_PROPERTY, unifiedQuery); + } else { + App.get().queryComboBox.putClientProperty(App.ACTIVE_BOOKMARK_QUERY_PROPERTY, null); + } + } else { + App.get().queryComboBox.putClientProperty(App.ACTIVE_BOOKMARK_QUERY_PROPERTY, null); + } + if (!clearing) App.get().appletListener.updateFileListing(); @@ -87,6 +104,62 @@ public void valueChanged(TreeSelectionEvent evt) { } + /** + * Checks if a bookmark has the same query across all cases in multicase mode. + * Returns the query if they're all identical, or null if they differ or don't exist. + */ + private String getUnifiedBookmarkQuery(String bookmarkName) { + IMultiBookmarks mm = App.get().appCase.getMultiBookmarks(); + + if (!App.get().isMultiCase()) { + // Single case - just return the query + return mm.getBookmarkQuery(bookmarkName); + } + + // Multicase + if (!(mm instanceof MultiBitmapBookmarks)) { + return null; + } + + // Check bookmark query for the bookmark in all cases + String unifiedQuery = null; + boolean foundBookmark = false; + for (IIPEDSource source : App.get().appCase.getAtomicSources()) { + IBookmarks bookmarks = source.getBookmarks(); + + // Get bookmark ID for the bookmark in this case + int bookmarkId = bookmarks.getBookmarkId(bookmarkName); + if (bookmarkId == -1) { + continue; // the bookmark does not exist in this case + } + + String caseQuery = bookmarks.getBookmarkQuery(bookmarkId); + + // Normalize null/empty to null for comparison + String normalizedCaseQuery = (caseQuery != null && !caseQuery.trim().isEmpty()) ? caseQuery.trim() : null; + + if (!foundBookmark) { + // First case where the bookmark exists + unifiedQuery = normalizedCaseQuery; + foundBookmark = true; + } else { + // Check if this case's query matches the unified query + if (unifiedQuery == null && normalizedCaseQuery == null) { + // Both null - continue + continue; + } else if (unifiedQuery == null || normalizedCaseQuery == null) { + // One is null, the other isn't - queries differ + return null; + } else if (!unifiedQuery.equals(normalizedCaseQuery)) { + // Queries differ between cases + return null; + } + } + } + + return unifiedQuery; + } + public void updateModelAndSelection() { updatingSelection = true; @@ -210,26 +283,94 @@ public boolean hasFiltersApplied() { } class BookMarkFilter implements IResultSetFilter, IMutableFilter, IBitmapFilter { - Set bookmark; + Set bookmarks; - public BookMarkFilter(Set bookmark2) { - this.bookmark = bookmark2; + public BookMarkFilter(Set bookmarks) { + this.bookmarks = bookmarks; } @Override public IMultiSearchResult filterResult(IMultiSearchResult src) throws ParseException, QueryNodeException, IOException { - return (MultiSearchResult) App.get().appCase.getMultiBookmarks().filterBookmarks(src, (Set) bookmark); + IMultiSearchResult bookmarkedItems = App.get().appCase.getMultiBookmarks().filterBookmarks(src, bookmarks); + + // Get the global query + org.apache.lucene.search.Query globalQuery = App.get().getQuery(); + + // Get the bookmark query if available + String bookmarkQueryStr = (String) App.get().queryComboBox.getClientProperty(App.ACTIVE_BOOKMARK_QUERY_PROPERTY); + org.apache.lucene.search.Query bookmarkQuery = null; + + if (bookmarkQueryStr != null && !bookmarkQueryStr.trim().isEmpty()) { + try { + bookmarkQuery = new iped.engine.search.QueryBuilder(App.get().appCase).getQuery(bookmarkQueryStr); + } catch (Exception e) { + e.printStackTrace(); + } + } + + // Combine queries for search execution + org.apache.lucene.search.Query combinedQuery = globalQuery; + + if (bookmarkQuery != null) { + if (globalQuery != null) { + // Combine both queries with AND logic + org.apache.lucene.search.BooleanQuery.Builder builder = new org.apache.lucene.search.BooleanQuery.Builder(); + builder.add(globalQuery, org.apache.lucene.search.BooleanClause.Occur.MUST); + builder.add(bookmarkQuery, org.apache.lucene.search.BooleanClause.Occur.MUST); + combinedQuery = builder.build(); + } else { + combinedQuery = bookmarkQuery; + } + } + + if (combinedQuery == null) { + return bookmarkedItems; + } + + // Execute the search using the combined query + iped.engine.search.IPEDSearcher searcher = new iped.engine.search.IPEDSearcher(App.get().appCase, combinedQuery); + IMultiSearchResult queryMatches = (IMultiSearchResult) searcher.search(); + + // Return the intersection of bookmarked items and search hits + return intersect(bookmarkedItems, queryMatches); + } + + /** + * Keeps only items that exist in both result sets. + */ + private IMultiSearchResult intersect(IMultiSearchResult bmkResults, IMultiSearchResult queryResults) { + // Iterate through the items in query results + java.util.Set queryIds = new java.util.HashSet<>(); + for (iped.data.IItemId id : queryResults.getIterator()) { + queryIds.add(id); + } + + // Iterate through the items in bookmark results + java.util.List finalIds = new java.util.ArrayList<>(); + java.util.List finalScores = new java.util.ArrayList<>(); + for (int i = 0; i < bmkResults.getLength(); i++) { + iped.data.IItemId bmkItem = bmkResults.getItem(i); + if (queryIds.contains(bmkItem)) { + finalIds.add(bmkItem); + finalScores.add(bmkResults.getScore(i)); + } + } + + return new iped.engine.search.MultiSearchResult( + finalIds.toArray(new iped.data.IItemId[0]), + ArrayUtils.toPrimitive(finalScores.toArray(new Float[0])) + ); } public String toString() { - return bookmark.toString(); + return bookmarks.toString(); } @Override public RoaringBitmap[] getBitmap() { IMultiBookmarks mm = App.get().appCase.getMultiBookmarks(); if (mm instanceof MultiBitmapBookmarks) { - return ((MultiBitmapBookmarks) mm).getBookmarksUnions(bookmark); + return ((MultiBitmapBookmarks) mm).getBookmarksUnions(bookmarks); } return null; } diff --git a/iped-app/src/main/java/iped/app/ui/UICaseSearchFilterListener.java b/iped-app/src/main/java/iped/app/ui/UICaseSearchFilterListener.java index 081c57edb6..a629567b40 100644 --- a/iped-app/src/main/java/iped/app/ui/UICaseSearchFilterListener.java +++ b/iped-app/src/main/java/iped/app/ui/UICaseSearchFilterListener.java @@ -75,16 +75,42 @@ public void onDone() { private void saveHighlightTerms() throws ParseException, QueryNodeException { Set highlightTerms = new QueryBuilder(App.get().appCase).getQueryStrings(caseSearcherFilter.getQueryText()); highlightTerms.addAll(App.get().metadataPanel.getHighlightTerms()); + + // Add bookmark query terms to highlight + String bookmarkQuery = (String) App.get().queryComboBox.getClientProperty(App.ACTIVE_BOOKMARK_QUERY_PROPERTY); + if (bookmarkQuery != null && !bookmarkQuery.trim().isEmpty()) { + Set bookmarkTerms = new QueryBuilder(App.get().appCase).getQueryStrings(bookmarkQuery); + highlightTerms.addAll(bookmarkTerms); + } + App.get().setHighlightTerms(highlightTerms); + // Build the highlight query Query highlightQuery = App.get().metadataPanel.getHighlightQuery(); + Query searchQuery = caseSearcherFilter.getSearcher().getQuery(); + + // Build the combined highlight query + BooleanQuery.Builder boolQuery = new BooleanQuery.Builder(); + if (highlightQuery != null) { - BooleanQuery.Builder boolQuery = new BooleanQuery.Builder(); boolQuery.add(highlightQuery, Occur.SHOULD); - boolQuery.add(caseSearcherFilter.getSearcher().getQuery(), Occur.SHOULD); - highlightQuery = boolQuery.build(); - } else - highlightQuery = caseSearcherFilter.getSearcher().getQuery(); + } + + if (searchQuery != null) { + boolQuery.add(searchQuery, Occur.SHOULD); + } + + // Add bookmark query to highlight + if (bookmarkQuery != null && !bookmarkQuery.trim().isEmpty()) { + try { + Query bmkQuery = new QueryBuilder(App.get().appCase).getQuery(bookmarkQuery); + boolQuery.add(bmkQuery, Occur.SHOULD); + } catch (Exception e) { + e.printStackTrace(); + } + } + + highlightQuery = boolQuery.build(); App.get().setQuery(highlightQuery); } diff --git a/iped-app/src/main/java/iped/app/ui/bookmarks/BookmarkListRenderer.java b/iped-app/src/main/java/iped/app/ui/bookmarks/BookmarkListRenderer.java index 4ea7d0e2fd..7ccf1979b4 100644 --- a/iped-app/src/main/java/iped/app/ui/bookmarks/BookmarkListRenderer.java +++ b/iped-app/src/main/java/iped/app/ui/bookmarks/BookmarkListRenderer.java @@ -3,12 +3,15 @@ import java.awt.Color; import java.awt.Component; +import javax.swing.Icon; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.ListCellRenderer; import javax.swing.UIManager; import iped.app.ui.App; +import iped.app.ui.BookmarksMismatchChecker; +import iped.app.ui.utils.AlertIcon; public class BookmarkListRenderer extends JLabel implements ListCellRenderer { private static final long serialVersionUID = 19720909122009L; @@ -40,8 +43,17 @@ public Component getListCellRendererComponent(JList li boolean isSelected, boolean cellHasFocus) { BookmarkAndKey bk = (BookmarkAndKey) value; setText(bk.toString()); + + // Get the bookmark color Color color = App.get().appCase.getMultiBookmarks().getBookmarkColor(bk.getName()); - setIcon(BookmarkIcon.getIcon(color)); + Icon baseIcon = BookmarkIcon.getIcon(color); + + // Check if there is a query mismatch and wrap with alert icon if that is the case + if (BookmarksMismatchChecker.get().queryMismatch(bk.getName())) { + setIcon(new AlertIcon(baseIcon)); + } else { + setIcon(baseIcon); + } setOpaque(true); diff --git a/iped-app/src/main/java/iped/app/ui/bookmarks/BookmarkTreeCellRenderer.java b/iped-app/src/main/java/iped/app/ui/bookmarks/BookmarkTreeCellRenderer.java index 645587fbaa..8797815fc3 100644 --- a/iped-app/src/main/java/iped/app/ui/bookmarks/BookmarkTreeCellRenderer.java +++ b/iped-app/src/main/java/iped/app/ui/bookmarks/BookmarkTreeCellRenderer.java @@ -8,8 +8,11 @@ import javax.swing.tree.DefaultTreeCellRenderer; import iped.app.ui.App; +import iped.app.ui.BookmarksMismatchChecker; import iped.app.ui.BookmarksTreeModel; import iped.app.ui.IconManager; +import iped.app.ui.Messages; +import iped.app.ui.utils.AlertIcon; import iped.data.IMultiBookmarks; import iped.utils.LocalizedFormat; @@ -40,7 +43,14 @@ public Component getTreeCellRendererComponent(JTree tree, Object value, boolean setIcon(BookmarkIcon.getIcon(BookmarkStandardColors.noBookmarksColor)); } else if (!value.equals(BookmarksTreeModel.ROOT)) { Color color = bookmarks.getBookmarkColor(name); - setIcon(BookmarkIcon.getIcon(color == null ? BookmarkStandardColors.defaultColor : color)); + Icon baseIcon = BookmarkIcon.getIcon(color == null ? BookmarkStandardColors.defaultColor : color); + if (!BookmarksMismatchChecker.get().queryMismatch(name)) { + setIcon(baseIcon); + } else { + setIcon(new AlertIcon(baseIcon)); + setToolTipText(Messages.getString("BookmarksManager.MismatchTooltip")); + } + int count = bookmarks.getBookmarkCount(name); if (count > 0) { setText(value + " (" + LocalizedFormat.format(count) + ")"); diff --git a/iped-app/src/main/java/iped/app/ui/bookmarks/BookmarksMismatchDialog.java b/iped-app/src/main/java/iped/app/ui/bookmarks/BookmarksMismatchDialog.java new file mode 100644 index 0000000000..14d5749074 --- /dev/null +++ b/iped-app/src/main/java/iped/app/ui/bookmarks/BookmarksMismatchDialog.java @@ -0,0 +1,114 @@ +package iped.app.ui.bookmarks; + +import javax.swing.*; +import javax.swing.border.EmptyBorder; + +import iped.app.ui.Messages; +import iped.data.IBookmarks; +import iped.data.IIPEDSource; +import iped.engine.data.IPEDSource; + +import java.awt.*; +import java.util.List; + +public class BookmarksMismatchDialog extends JDialog { + + public BookmarksMismatchDialog(JDialog parent, String bookmarkName, List sources) { + super(parent, ModalityType.APPLICATION_MODAL); + setTitle(Messages.getString("BookmarksManager.Mismatch.Title")); + setLocationRelativeTo(parent); + setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE); + + setLayout(new BorderLayout()); + + // Header panel + JPanel headerPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); + headerPanel.setBorder(new EmptyBorder(10, 10, 0, 10)); + JLabel headerLabel = new JLabel(Messages.getString("BookmarksManager.Bookmark") + ": " + bookmarkName); + headerPanel.add(headerLabel); + add(headerPanel, BorderLayout.NORTH); + + // Container for all case panels + JPanel mainList = new JPanel(); + mainList.setLayout(new BoxLayout(mainList, BoxLayout.Y_AXIS)); + mainList.setBorder(new EmptyBorder(10, 10, 10, 10)); + // mainList.add(Box.createVerticalStrut(10)); + + for (IIPEDSource source : sources) { + if (!(source instanceof IPEDSource)) + continue; + + IBookmarks bookmarks = source.getBookmarks(); + + // Get bookmark ID for the bookmark in this case + int bookmarkId = bookmarks.getBookmarkId(bookmarkName); + if (bookmarkId == -1) { + continue; // the bookmark does not exist in this case + } + + IPEDSource ipedCase = (IPEDSource) source; + int caseId = ipedCase.getSourceId() + 1; + String casePath = ipedCase.getCaseDir().getAbsolutePath(); + String caseComments = bookmarks.getBookmarkComment(bookmarkId); + String caseQuery = bookmarks.getBookmarkQuery(bookmarkId); + + // Case Panel + JPanel casePanel = new JPanel(new BorderLayout(5, 5)); + casePanel.setAlignmentX(Component.LEFT_ALIGNMENT); + JPanel infoPanel = new JPanel(new GridLayout(2, 1)); + + // Case ID Label + infoPanel.add(new JLabel(Messages.getString("App.Case") + ": " + caseId)); + + // Case Path TextField + JTextField pathTextField = new JTextField(casePath); + pathTextField.setToolTipText(Messages.getString("BookmarksManager.NewBookmark.Tip")); + pathTextField.setEditable(false); + // pathTextField.setBorder(null); + pathTextField.setFont(new Font("Monospaced", Font.PLAIN, 11)); + infoPanel.add(pathTextField); + + // Comments TextArea + JTextArea commentsArea = new JTextArea(caseComments != null ? caseComments : ""); + commentsArea.setRows(2); + commentsArea.setLineWrap(true); + commentsArea.setWrapStyleWord(true); + commentsArea.setEditable(false); + commentsArea.setToolTipText(Messages.getString("BookmarksManager.CommentsTooltip")); //$NON-NLS-1$ + JScrollPane commentsScroll = new JScrollPane(commentsArea); + commentsScroll.setPreferredSize(new Dimension(300, 50)); + + // Query TextArea + JTextArea queryArea = new JTextArea(caseQuery != null ? caseQuery : ""); + queryArea.setRows(2); + queryArea.setLineWrap(true); + queryArea.setWrapStyleWord(true); + queryArea.setEditable(false); + queryArea.setToolTipText(Messages.getString("BookmarksManager.QueryTooltip")); //$NON-NLS-1$ + JScrollPane queryScroll = new JScrollPane(queryArea); + queryScroll.setPreferredSize(new Dimension(300, 50)); + queryArea.setForeground(Color.RED); + + casePanel.add(infoPanel, BorderLayout.NORTH); + casePanel.add(commentsScroll, BorderLayout.CENTER); + casePanel.add(queryScroll, BorderLayout.SOUTH); + mainList.add(casePanel); + mainList.add(Box.createVerticalStrut(15)); + } + + JScrollPane mainScroll = new JScrollPane(mainList); + mainScroll.getVerticalScrollBar().setUnitIncrement(16); + + add(mainScroll, BorderLayout.CENTER); + + JButton closeBtn = new JButton(Messages.getString("Ok")); + closeBtn.addActionListener(e -> dispose()); + JPanel btnPanel = new JPanel(); + btnPanel.add(closeBtn); + add(btnPanel, BorderLayout.SOUTH); + + pack(); + setSize(getWidth() + 20, 510); + setLocationRelativeTo(parent); + } +} diff --git a/iped-app/src/main/java/iped/app/ui/utils/AlertIcon.java b/iped-app/src/main/java/iped/app/ui/utils/AlertIcon.java new file mode 100644 index 0000000000..b836e7f56f --- /dev/null +++ b/iped-app/src/main/java/iped/app/ui/utils/AlertIcon.java @@ -0,0 +1,44 @@ +package iped.app.ui.utils; + +import javax.swing.*; +import java.awt.*; + +public class AlertIcon implements Icon { + private final Icon base; + private final Color alertColor = Color.RED; + + public AlertIcon(Icon base) { + this.base = base; + } + + @Override + public void paintIcon(Component c, Graphics g, int x, int y) { + // 1. Draw the original bookmark icon + base.paintIcon(c, g, x, y); + + // 2. Prepare to draw the exclamation circle + Graphics2D g2 = (Graphics2D) g.create(); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + int size = 10; // size of the alert circle + int posX = x + getIconWidth() - size + 2; + int posY = y - 1; + + // Draw red circle + g2.setColor(alertColor); + g2.fillOval(posX, posY, size, size); + + // Draw white exclamation mark + g2.setColor(Color.WHITE); + g2.setFont(new Font("SansSerif", Font.BOLD, 8)); + g2.drawString("!", posX + size/2, posY + size - 2); + + g2.dispose(); + } + + @Override + public int getIconWidth() { return base.getIconWidth(); } + + @Override + public int getIconHeight() { return base.getIconHeight(); } +} \ No newline at end of file diff --git a/iped-engine/src/main/java/iped/engine/data/BitmapBookmarks.java b/iped-engine/src/main/java/iped/engine/data/BitmapBookmarks.java index 6e42fa1dd1..ed68a8adee 100644 --- a/iped-engine/src/main/java/iped/engine/data/BitmapBookmarks.java +++ b/iped-engine/src/main/java/iped/engine/data/BitmapBookmarks.java @@ -66,6 +66,7 @@ public class BitmapBookmarks implements IBookmarks { HashMap bookmarks; private TreeMap bookmarkNames = new TreeMap(); private TreeMap bookmarkComments = new TreeMap(); + private Map bookmarkQueries = new TreeMap(); private TreeMap bookmarkKeyStrokes = new TreeMap(); private Set reportBookmarks = new TreeSet(); TreeMap bookmarkColors = new TreeMap(); @@ -261,6 +262,7 @@ public synchronized int newBookmark(String bookmarkName) { bookmarkNames.put(bookmarkId, bookmarkName); bookmarkComments.put(bookmarkId, null); + bookmarkQueries.put(bookmarkId, null); bookmarkKeyStrokes.put(bookmarkId, null); if (bookmarkColors == null) bookmarkColors = new TreeMap(); @@ -276,6 +278,7 @@ public synchronized void delBookmark(int bookmark) { return; bookmarkNames.remove(bookmark); bookmarkComments.remove(bookmark); + bookmarkQueries.remove(bookmark); bookmarkKeyStrokes.remove(bookmark); if (bookmarkColors == null) bookmarkColors = new TreeMap(); @@ -310,6 +313,14 @@ public String getBookmarkComment(int bookmarkId) { return bookmarkComments.get(bookmarkId); } + public void setBookmarkQuery(int bookmarkId, String query) { + bookmarkQueries.put(bookmarkId, query); + } + + public String getBookmarkQuery(int bookmarkId) { + return bookmarkQueries.get(bookmarkId); + } + public synchronized void setBookmarkKeyStroke(int bookmarkId, KeyStroke key) { bookmarkKeyStrokes.put(bookmarkId, key); } @@ -476,6 +487,7 @@ public synchronized void loadState(File file) throws IOException, ClassNotFoundE this.typedWords = state.typedWords; this.bookmarkNames = state.bookmarkNames; this.bookmarkComments = state.bookmarkComments; + this.bookmarkQueries = state.bookmarkQueries; this.bookmarkKeyStrokes = state.bookmarkKeyStrokes; this.reportBookmarks = state.reportBookmarks; this.bookmarkColors = state.bookmarkColors; @@ -510,6 +522,7 @@ public synchronized void loadState(File file) throws IOException, ClassNotFoundE this.typedWords = state.typedWords; this.bookmarkNames = state.bookmarkNames; this.bookmarkComments = state.bookmarkComments; + this.bookmarkQueries = state.bookmarkQueries; this.bookmarkKeyStrokes = state.bookmarkKeyStrokes; this.reportBookmarks = state.reportBookmarks; this.bookmarkColors = state.bookmarkColors; diff --git a/iped-engine/src/main/java/iped/engine/data/Bookmarks.java b/iped-engine/src/main/java/iped/engine/data/Bookmarks.java index e57be113e5..e14b1856ed 100644 --- a/iped-engine/src/main/java/iped/engine/data/Bookmarks.java +++ b/iped-engine/src/main/java/iped/engine/data/Bookmarks.java @@ -61,6 +61,7 @@ public class Bookmarks implements IBookmarks { ArrayList bookmarks; TreeMap bookmarkNames = new TreeMap(); TreeMap bookmarkComments = new TreeMap(); + TreeMap bookmarkQueries = new TreeMap(); TreeMap bookmarkKeyStrokes = new TreeMap(); TreeMap bookmarkColors = new TreeMap(); Set reportBookmarks = new TreeSet(); @@ -278,6 +279,7 @@ public synchronized int newBookmark(String bookmarkName) { bookmarkNames.put(bookmarkId, bookmarkName); bookmarkComments.put(bookmarkId, null); + bookmarkQueries.put(bookmarkId, null); bookmarkKeyStrokes.put(bookmarkId, null); if (bookmarkColors == null) bookmarkColors = new TreeMap(); @@ -291,6 +293,7 @@ public synchronized void delBookmark(int bookmark) { return; bookmarkNames.remove(bookmark); bookmarkComments.remove(bookmark); + bookmarkQueries.remove(bookmark); bookmarkKeyStrokes.remove(bookmark); if (bookmarkColors == null) bookmarkColors = new TreeMap(); @@ -331,6 +334,14 @@ public String getBookmarkComment(int bookmarkId) { return bookmarkComments.get(bookmarkId); } + public void setBookmarkQuery(int bookmarkId, String query) { + bookmarkQueries.put(bookmarkId, query); + } + + public String getBookmarkQuery(int bookmarkId) { + return bookmarkQueries.get(bookmarkId); + } + public synchronized void setBookmarkColor(int bookmarkId, Color color) { if (bookmarkColors == null) bookmarkColors = new TreeMap(); @@ -515,6 +526,7 @@ public synchronized void loadState(File file) throws IOException, ClassNotFoundE this.selectedItens = state.selectedItens; this.bookmarkNames = state.bookmarkNames; this.bookmarkComments = state.bookmarkComments; + this.bookmarkQueries = state.bookmarkQueries; this.bookmarkKeyStrokes = state.bookmarkKeyStrokes; this.reportBookmarks = state.reportBookmarks; this.bookmarkColors = state.bookmarkColors; diff --git a/iped-engine/src/main/java/iped/engine/data/MultiBitmapBookmarks.java b/iped-engine/src/main/java/iped/engine/data/MultiBitmapBookmarks.java index 6d42fb5bbf..31cdb3ac80 100644 --- a/iped-engine/src/main/java/iped/engine/data/MultiBitmapBookmarks.java +++ b/iped-engine/src/main/java/iped/engine/data/MultiBitmapBookmarks.java @@ -215,6 +215,20 @@ public String getBookmarkComment(String bookmarkName) { return null; } + public void setBookmarkQuery(String bookmarkName, String query) { + for (IBookmarks m : map.values()) + m.setBookmarkQuery(m.getBookmarkId(bookmarkName), query); + } + + public String getBookmarkQuery(String bookmarkName) { + for (IBookmarks m : map.values()) { + String query = m.getBookmarkQuery(m.getBookmarkId(bookmarkName)); + if (query != null) + return query; + } + return null; + } + public void setBookmarkKeyStroke(String bookmarkName, KeyStroke key) { for (IBookmarks m : map.values()) m.setBookmarkKeyStroke(m.getBookmarkId(bookmarkName), key); diff --git a/iped-engine/src/main/java/iped/engine/data/MultiBookmarks.java b/iped-engine/src/main/java/iped/engine/data/MultiBookmarks.java index a1374aca6c..fc2f5698ac 100644 --- a/iped-engine/src/main/java/iped/engine/data/MultiBookmarks.java +++ b/iped-engine/src/main/java/iped/engine/data/MultiBookmarks.java @@ -209,6 +209,20 @@ public String getBookmarkComment(String bookmarkName) { return null; } + public void setBookmarkQuery(String bookmarkName, String query) { + for (IBookmarks m : map.values()) + m.setBookmarkQuery(m.getBookmarkId(bookmarkName), query); + } + + public String getBookmarkQuery(String bookmarkName) { + for (IBookmarks m : map.values()) { + String query = m.getBookmarkQuery(m.getBookmarkId(bookmarkName)); + if (query != null) + return query; + } + return null; + } + public void setBookmarkColor(String bookmarkName, Color color) { for (IBookmarks m : map.values()) { int bid = m.getBookmarkId(bookmarkName); From 716b009f314dda6ba2bb152fa3a5f259a651dd9b Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Mon, 12 Jan 2026 00:51:14 -0300 Subject: [PATCH 2/3] #2452: Remove unused import --- iped-app/src/main/java/iped/app/ui/BookmarksManager.java | 1 - 1 file changed, 1 deletion(-) diff --git a/iped-app/src/main/java/iped/app/ui/BookmarksManager.java b/iped-app/src/main/java/iped/app/ui/BookmarksManager.java index 1249065bc9..67cfc99acd 100644 --- a/iped-app/src/main/java/iped/app/ui/BookmarksManager.java +++ b/iped-app/src/main/java/iped/app/ui/BookmarksManager.java @@ -68,7 +68,6 @@ import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.TermQuery; import org.apache.lucene.util.BytesRef; -import org.checkerframework.checker.units.qual.A; import org.slf4j.Logger; import org.slf4j.LoggerFactory; From 224f54e4accfeea2789e67892f994246da6e6655 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Wed, 14 Jan 2026 19:42:01 -0300 Subject: [PATCH 3/3] #2452: Add bookmark query panel --- .../iped-desktop-messages.properties | 1 + .../iped-desktop-messages_de_DE.properties | 1 + .../iped-desktop-messages_es_AR.properties | 1 + .../iped-desktop-messages_fr_FR.properties | 1 + .../iped-desktop-messages_it_IT.properties | 1 + .../iped-desktop-messages_pt_BR.properties | 1 + iped-app/src/main/java/iped/app/ui/App.java | 59 ++++++++++++++++++- .../java/iped/app/ui/BookmarksManager.java | 53 ++++++++++++++--- .../iped/app/ui/BookmarksTreeListener.java | 3 + 9 files changed, 109 insertions(+), 12 deletions(-) diff --git a/iped-app/resources/localization/iped-desktop-messages.properties b/iped-app/resources/localization/iped-desktop-messages.properties index 81f5db2b7e..b3931a9d88 100644 --- a/iped-app/resources/localization/iped-desktop-messages.properties +++ b/iped-app/resources/localization/iped-desktop-messages.properties @@ -86,6 +86,7 @@ BookmarksManager.ConfirmUpdate=Updates will be applied to associated cases.\nDo BookmarksManager.QueryTooltip=Bookmark query BookmarksManager.QueryTooltip.Multicase=Bookmark query (as stored in the first associated case) BookmarksManager.AlreadyExists=Bookmark name already exists! +BookmarksManager.ShowBookmarkQueryPanel=Show bookmark query panel BookmarksTreeModel.NoBookmarks=[No Bookmarks] BookmarksTreeModel.RootName=Bookmarks CategoryTreeModel.RootName=Categories diff --git a/iped-app/resources/localization/iped-desktop-messages_de_DE.properties b/iped-app/resources/localization/iped-desktop-messages_de_DE.properties index 91b9ed9e9b..c8b71591f2 100644 --- a/iped-app/resources/localization/iped-desktop-messages_de_DE.properties +++ b/iped-app/resources/localization/iped-desktop-messages_de_DE.properties @@ -86,6 +86,7 @@ BookmarksManager.ConfirmUpdate=Aktualisierungen werden auf die verknüpften Fäl BookmarksManager.QueryTooltip=Lesezeichen-Abfrage BookmarksManager.QueryTooltip.Multicase=Lesezeichen-Abfrage (wie im ersten zugeordneten Fall gespeichert) BookmarksManager.AlreadyExists=Lesezeichen ist bereits vorhanden! +BookmarksManager.ShowBookmarkQueryPanel=Zeige Lesezeichen-Abfrage-Panels BookmarksTreeModel.NoBookmarks=[Keine Lesezeichen] BookmarksTreeModel.RootName=Lesezeichen CategoryTreeModel.RootName=Kategorien diff --git a/iped-app/resources/localization/iped-desktop-messages_es_AR.properties b/iped-app/resources/localization/iped-desktop-messages_es_AR.properties index 41b9ba2b57..fb60838fc8 100644 --- a/iped-app/resources/localization/iped-desktop-messages_es_AR.properties +++ b/iped-app/resources/localization/iped-desktop-messages_es_AR.properties @@ -86,6 +86,7 @@ BookmarksManager.ConfirmUpdate=Las actualizaciones se aplicarán a los casos aso BookmarksManager.QueryTooltip=Consulta asociada al marcador BookmarksManager.QueryTooltip.Multicase=Consulta asociada al marcador (como se almacena en el primer caso asociado) BookmarksManager.AlreadyExists=¡El nombre del marcador ya existe! +BookmarksManager.ShowBookmarkQueryPanel=Mostrar panel de consulta del marcador BookmarksTreeModel.NoBookmarks=[Sin marcadores] BookmarksTreeModel.RootName=Marcadores CategoryTreeModel.RootName=Categorías diff --git a/iped-app/resources/localization/iped-desktop-messages_fr_FR.properties b/iped-app/resources/localization/iped-desktop-messages_fr_FR.properties index bd0b009f93..9247bf3fa2 100644 --- a/iped-app/resources/localization/iped-desktop-messages_fr_FR.properties +++ b/iped-app/resources/localization/iped-desktop-messages_fr_FR.properties @@ -86,6 +86,7 @@ BookmarksManager.ConfirmUpdate=Les mises à jour seront appliquées aux cas asso BookmarksManager.QueryTooltip=Requête associée au favori BookmarksManager.QueryTooltip.Multicase=Requête associée au favori (comme stockée dans le premier cas associé) BookmarksManager.AlreadyExists=Nom du favori existe déjà \! +BookmarksManager.ShowBookmarkQueryPanel=Afficher le panneau de requête du favori BookmarksTreeModel.NoBookmarks=[Aucun favori] BookmarksTreeModel.RootName=Favoris CategoryTreeModel.RootName=Catégories diff --git a/iped-app/resources/localization/iped-desktop-messages_it_IT.properties b/iped-app/resources/localization/iped-desktop-messages_it_IT.properties index 753a108a44..9cb2f9dd9f 100644 --- a/iped-app/resources/localization/iped-desktop-messages_it_IT.properties +++ b/iped-app/resources/localization/iped-desktop-messages_it_IT.properties @@ -86,6 +86,7 @@ BookmarksManager.ConfirmUpdate=Aggiornamenti saranno applicati ai casi associati BookmarksManager.QueryTooltip=Query associata al segnalibro BookmarksManager.QueryTooltip.Multicase=Query associata al segnalibro (come memorizzata nel primo caso associato) BookmarksManager.AlreadyExists=Il nome del segnalibro esiste già! +BookmarksManager.ShowBookmarkQueryPanel=Mostra pannello di query del segnalibro BookmarksTreeModel.NoBookmarks=[Nessun segnalibro] BookmarksTreeModel.RootName=Segnalibri CategoryTreeModel.RootName=Categorie diff --git a/iped-app/resources/localization/iped-desktop-messages_pt_BR.properties b/iped-app/resources/localization/iped-desktop-messages_pt_BR.properties index 8c2d9a3bfc..68e1ff1a2f 100644 --- a/iped-app/resources/localization/iped-desktop-messages_pt_BR.properties +++ b/iped-app/resources/localization/iped-desktop-messages_pt_BR.properties @@ -86,6 +86,7 @@ BookmarksManager.ConfirmUpdate=Atualizações serão aplicadas aos casos associa BookmarksManager.QueryTooltip=Consulta associada ao marcador BookmarksManager.QueryTooltip.Multicase=Consulta associada ao marcador (conforme armazenado no primeiro caso associado) BookmarksManager.AlreadyExists=Nome de marcador já existente! +BookmarksManager.ShowBookmarkQueryPanel=Mostrar painel de consulta do marcador BookmarksTreeModel.NoBookmarks=[Sem Marcadores] BookmarksTreeModel.RootName=Marcadores CategoryTreeModel.RootName=Categorias diff --git a/iped-app/src/main/java/iped/app/ui/App.java b/iped-app/src/main/java/iped/app/ui/App.java index 3fb3b46357..223517c891 100644 --- a/iped-app/src/main/java/iped/app/ui/App.java +++ b/iped-app/src/main/java/iped/app/ui/App.java @@ -69,6 +69,7 @@ import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.JTable; +import javax.swing.JTextField; import javax.swing.JTree; import javax.swing.KeyStroke; import javax.swing.RowSorter.SortKey; @@ -249,6 +250,10 @@ public class App extends JFrame implements WindowListener, IMultiSearchResultPro private JScrollPane hitsScroll, subItemScroll, parentItemScroll, duplicatesScroll, referencesScroll, referencedByScroll; JScrollPane viewerScroll, resultsScroll, galleryScroll; JPanel topPanel; + JPanel searchPanel; + JPanel bookmarkQueryPanel; + JPanel searchAndBookmarkQueryPanel; + JTextField bookmarkQueryField; ClearFilterButton clearAllFilters; boolean verticalLayout = false; @@ -285,7 +290,7 @@ public class App extends JFrame implements WindowListener, IMultiSearchResultPro final static String FILTRO_TODOS = Messages.getString("App.NoFilter"); //$NON-NLS-1$ final static String FILTRO_SELECTED = Messages.getString("App.Checked"); //$NON-NLS-1$ public final static String SEARCH_TOOL_TIP = Messages.getString("App.SearchBoxTip"); //$NON-NLS-1$ - public final static String ACTIVE_BOOKMARK_QUERY_PROPERTY = "active_bookmark_query"; //$NON-NLS-1$ + public final static String ACTIVE_BOOKMARK_QUERY_PROPERTY = "ActiveBookmarkQuery"; //$NON-NLS-1$ static int MAX_HITS = 10000; @@ -555,13 +560,37 @@ public void createGUI() { similarFacesFilterPanel = new SimilarFacesFilterPanel(); similarFacesFilterPanel.setVisible(false); + // Search panel + searchPanel = new JPanel(); + searchPanel.setLayout(new BoxLayout(searchPanel, BoxLayout.LINE_AXIS)); + searchPanel.add(new JLabel(tab + Messages.getString("App.SearchLabel"))); //$NON-NLS-1$ + searchPanel.add(queryComboBox); + + // Bookmark query panel + bookmarkQueryPanel = new JPanel(); + bookmarkQueryPanel.setLayout(new BoxLayout(bookmarkQueryPanel, BoxLayout.LINE_AXIS)); + bookmarkQueryPanel.setBorder(BorderFactory.createEmptyBorder(2, 0, 0,0)); + bookmarkQueryPanel.setVisible(false); + bookmarkQueryField = new JTextField(); + bookmarkQueryField.setEditable(false); + bookmarkQueryField.setForeground(Color.GRAY); + Border lineBorder = BorderFactory.createLineBorder(alertColor, 2, true); + Border paddingBorder = BorderFactory.createEmptyBorder(1, 3, 1, 3); + bookmarkQueryField.setBorder(BorderFactory.createCompoundBorder(lineBorder, paddingBorder)); + bookmarkQueryPanel.add(new JLabel(tab + Messages.getString("BookmarksManager.QueryTooltip") + ": ")); //$NON-NLS-1$ + bookmarkQueryPanel.add(bookmarkQueryField); + + // Container panel for Search panel and Bookmark query panel, so they share space equally + searchAndBookmarkQueryPanel = new JPanel(); + searchAndBookmarkQueryPanel.setLayout(new BorderLayout()); + searchAndBookmarkQueryPanel.add(searchPanel, BorderLayout.CENTER); + topPanel.add(filterComboBox); topPanel.add(filterDuplicates); topPanel.add(clearAllFilters); topPanel.add(similarImageFilterPanel); topPanel.add(similarFacesFilterPanel); - topPanel.add(new JLabel(tab + Messages.getString("App.SearchLabel"))); //$NON-NLS-1$ - topPanel.add(queryComboBox); + topPanel.add(searchAndBookmarkQueryPanel); topPanel.add(optionsButton); if (processingManager != null) topPanel.add(updateCaseData); @@ -1685,6 +1714,30 @@ public void repaintAllTableViews() { referencedByTable.repaint(); } + public void updateBookmarkQueryPanelVisibility(String query) { + // Hide bookmark panel if bookmark query mode is 'hidden' or if there is no active bookmark query + // Adjust container panel layout accordingly + if (BookmarksManager.isHiddenBookmarkQueryMode()) { + bookmarkQueryField.setText(""); + bookmarkQueryPanel.setVisible(false); + searchAndBookmarkQueryPanel.remove(bookmarkQueryPanel); + } else { + if (query != null && !query.trim().isEmpty()) { + bookmarkQueryField.setText(query); + bookmarkQueryField.setCaretPosition(0); + bookmarkQueryPanel.setVisible(true); + bookmarkQueryPanel.setPreferredSize(new Dimension(searchAndBookmarkQueryPanel.getWidth() / 2, 0)); + searchAndBookmarkQueryPanel.add(bookmarkQueryPanel, BorderLayout.WEST); + } else { + bookmarkQueryField.setText(""); + bookmarkQueryPanel.setVisible(false); + searchAndBookmarkQueryPanel.remove(bookmarkQueryPanel); + } + } + searchAndBookmarkQueryPanel.revalidate(); + searchAndBookmarkQueryPanel.repaint(); + } + private class SpaceKeyListener extends KeyAdapter { @Override diff --git a/iped-app/src/main/java/iped/app/ui/BookmarksManager.java b/iped-app/src/main/java/iped/app/ui/BookmarksManager.java index 67cfc99acd..4c67d3985c 100644 --- a/iped-app/src/main/java/iped/app/ui/BookmarksManager.java +++ b/iped-app/src/main/java/iped/app/ui/BookmarksManager.java @@ -99,12 +99,16 @@ public class BookmarksManager implements ActionListener, ListSelectionListener, public static final int maxBookmarkNameLength = 256; + private static boolean hiddenBookmarkQueryMode = false; + JDialog dialog = new JDialog(App.get()); - JLabel msg = new JLabel(Messages.getString("BookmarksManager.Dataset")); //$NON-NLS-1$ + JLabel options = new JLabel(Messages.getString("App.Options") + ":"); //$NON-NLS-1$ + JLabel dataset = new JLabel(Messages.getString("BookmarksManager.Dataset")); //$NON-NLS-1$ JRadioButton highlighted = new JRadioButton(); JRadioButton checked = new JRadioButton(); ButtonGroup group = new ButtonGroup(); - JCheckBox duplicates = new JCheckBox(); + JCheckBox duplicates = new JCheckBox(Messages.getString("BookmarksManager.AddDuplicates")); //$NON-NLS-1$ + JCheckBox showBookmarkQueryModeCheckbox = new JCheckBox(Messages.getString("BookmarksManager.ShowBookmarkQueryPanel")); //$NON-NLS-1$ JButton butAdd = new JButton(Messages.getString("BookmarksManager.Add")); //$NON-NLS-1$ JButton butRemove = new JButton(Messages.getString("BookmarksManager.Remove")); //$NON-NLS-1$ JButton butEdit = new JButton(Messages.getString("BookmarksManager.Edit")); //$NON-NLS-1$ @@ -144,16 +148,31 @@ public static void updateCounters() { + LocalizedFormat.format(App.get().appCase.getMultiBookmarks().getTotalChecked()) + ")"); //$NON-NLS-1$ } + public static boolean isHiddenBookmarkQueryMode() { + return hiddenBookmarkQueryMode; + } + + public static void setHiddenBookmarkQueryMode(boolean hidden) { + hiddenBookmarkQueryMode = hidden; + // Update bookmark query panel visibility + String activeQuery = (String) App.get().queryComboBox.getClientProperty(App.ACTIVE_BOOKMARK_QUERY_PROPERTY); + if (hidden) { + App.get().updateBookmarkQueryPanelVisibility(null); + } else { + App.get().updateBookmarkQueryPanelVisibility(activeQuery); + } + } + private BookmarksManager() { dialog.setTitle(Messages.getString("BookmarksManager.Title")); //$NON-NLS-1$ - dialog.setSize(480, 480); + dialog.setSize(540, 540); group.add(highlighted); group.add(checked); highlighted.setSelected(true); - duplicates.setText(Messages.getString("BookmarksManager.AddDuplicates")); //$NON-NLS-1$ duplicates.setSelected(false); + showBookmarkQueryModeCheckbox.setSelected(!hiddenBookmarkQueryMode); updateList(); @@ -178,8 +197,12 @@ private BookmarksManager() { butMismatch.setForeground(Color.RED); butMismatch.setVisible(false); - JPanel top = new JPanel(new GridLayout(3, 2, 0, 5)); - top.add(msg); + JPanel top = new JPanel(new GridLayout(5, 2, 0, 5)); + top.add(options); + top.add(new JLabel()); + top.add(showBookmarkQueryModeCheckbox); + top.add(new JLabel()); + top.add(dataset); top.add(new JLabel()); top.add(highlighted); top.add(checked); @@ -240,6 +263,13 @@ private BookmarksManager() { pane.add(center, BorderLayout.CENTER); dialog.getContentPane().add(pane); + showBookmarkQueryModeCheckbox.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + setHiddenBookmarkQueryMode(!showBookmarkQueryModeCheckbox.isSelected()); + } + }); + butAdd.addActionListener(this); butUpdate.addActionListener(this); butRemove.addActionListener(this); @@ -518,11 +548,16 @@ public void actionPerformed(final ActionEvent evt) { BookmarksController.get().updateUI(); // Check if the updated bookmark is the only one selected in the bookmark tree - // If so, trigger a refresh to apply the new query highlighting + // If so, trigger a refresh to update the bookmark query panel and to apply the new query highlighting Set selectedBookmarks = App.get().bookmarksListener.getSelectedBookmarkNames(); - if (selectedBookmarks.size() == 1 && selectedBookmarks.contains(bookmarkName)) { - // Update the client property with the new query + if (App.get().bookmarksTree.getSelectionCount() == 1 && selectedBookmarks.size() == 1 && selectedBookmarks.contains(bookmarkName)) { String normalizedQuery = (newQuery != null && !newQuery.isEmpty()) ? newQuery : null; + + // Update the bookmark query panel + if (!BookmarksManager.isHiddenBookmarkQueryMode()) + App.get().updateBookmarkQueryPanelVisibility(normalizedQuery); + + // Update the client property with the new query App.get().queryComboBox.putClientProperty(App.ACTIVE_BOOKMARK_QUERY_PROPERTY, normalizedQuery); // Trigger file listing update to refresh with new bookmark query diff --git a/iped-app/src/main/java/iped/app/ui/BookmarksTreeListener.java b/iped-app/src/main/java/iped/app/ui/BookmarksTreeListener.java index d797468066..7235b3c4e2 100644 --- a/iped-app/src/main/java/iped/app/ui/BookmarksTreeListener.java +++ b/iped-app/src/main/java/iped/app/ui/BookmarksTreeListener.java @@ -86,11 +86,14 @@ public void valueChanged(TreeSelectionEvent evt) { String bookmarkName = selected.toString(); String unifiedQuery = getUnifiedBookmarkQuery(bookmarkName); App.get().queryComboBox.putClientProperty(App.ACTIVE_BOOKMARK_QUERY_PROPERTY, unifiedQuery); + App.get().updateBookmarkQueryPanelVisibility(unifiedQuery); } else { App.get().queryComboBox.putClientProperty(App.ACTIVE_BOOKMARK_QUERY_PROPERTY, null); + App.get().updateBookmarkQueryPanelVisibility(null); } } else { App.get().queryComboBox.putClientProperty(App.ACTIVE_BOOKMARK_QUERY_PROPERTY, null); + App.get().updateBookmarkQueryPanelVisibility(null); } if (!clearing)