diff --git a/trunk/mainwindow.cpp b/trunk/mainwindow.cpp index 2f8c770..0cca645 100644 --- a/trunk/mainwindow.cpp +++ b/trunk/mainwindow.cpp @@ -1,788 +1,932 @@ /******************************************************************************* * fred Copyright (c) 2011-2013 by Gillen Daniel * * * * Forensic Registry EDitor (fred) is a cross-platform M$ registry hive editor * * with special feautures useful during forensic analysis. * * * * This program is free software: you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the Free * * Software Foundation, either version 3 of the License, or (at your option) * * any later version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT * * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * * more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * *******************************************************************************/ #ifndef FRED_REPORT_TEMPLATE_DIR #ifndef __MINGW32__ #define FRED_REPORT_TEMPLATE_DIR "/usr/share/fred/report_templates/" #else #define FRED_REPORT_TEMPLATE_DIR ".\\report_templates\\" #endif #endif #include #include #include #include #include #include #include #include #include #include "mainwindow.h" #include "ui_mainwindow.h" #include "dlgabout.h" #include "dlgkeydetails.h" #include "dlgreportchooser.h" #include "dlgreportviewer.h" #include "dlgsearch.h" #include "dlgpreferences.h" #include "compileinfo.h" /******************************************************************************* * Public ******************************************************************************/ +/* + * Constructor + */ MainWindow::MainWindow(ArgParser *p_arg_parser) : QMainWindow(0), ui(new Ui::MainWindow) { ui->setupUi(this); // Initialize private vars this->p_args=p_arg_parser; this->p_hive=new RegistryHive(this); this->is_hive_open=false; this->p_reg_node_tree_model=NULL; this->p_reg_node_tree_model_proxy=NULL; this->p_reg_key_table_model=NULL; this->p_search_thread=NULL; this->search_result_widgets.clear(); // Init and load settings this->p_settings=new Settings(this); this->is_hive_writable=!this->p_settings->GetOpenHivesReadOnly(); // Set main window size QByteArray geometry=this->p_settings->GetWindowGeometry("MainWindow"); if(!geometry.isEmpty()) { // Restore saved geometry this->restoreGeometry(geometry); } else { // No saved geometry, calculate and set default int cur_screen=QApplication::desktop()->screenNumber(this); int window_width= QApplication::desktop()->availableGeometry(cur_screen).width()*0.5; int window_height= QApplication::desktop()->availableGeometry(cur_screen).height()*0.5; int window_x= (QApplication::desktop()->availableGeometry(cur_screen).width()/2)- (window_width/2); int window_y= (QApplication::desktop()->availableGeometry(cur_screen).height()/2)- (window_height/2); this->setGeometry(window_x, window_y, window_width, window_height); } // Create widgets this->p_horizontal_splitter=new QSplitter(); this->p_horizontal_splitter->setOrientation(Qt::Horizontal); this->p_node_tree=new RegistryNodeTree(this->p_horizontal_splitter); this->p_vertical_splitter=new QSplitter(this->p_horizontal_splitter); this->p_vertical_splitter->setOrientation(Qt::Vertical); this->p_key_table=new RegistryKeyTable(this->p_vertical_splitter); this->p_tab_widget=new TabWidget(this->p_vertical_splitter); this->p_hex_edit_widget=new HexEditWidget(); // Add hexedit page to tab_widget this->p_tab_widget->addTab(this->p_hex_edit_widget,tr("Hex viewer")); // Add widgets to their splitters this->p_vertical_splitter->addWidget(this->p_key_table); this->p_vertical_splitter->addWidget(this->p_tab_widget); this->p_horizontal_splitter->addWidget(this->p_node_tree); this->p_horizontal_splitter->addWidget(this->p_vertical_splitter); // Set stretch factors QSizePolicy node_tree_policy=this->p_node_tree->sizePolicy(); node_tree_policy.setHorizontalStretch(1); node_tree_policy.setVerticalStretch(100); this->p_node_tree->setSizePolicy(node_tree_policy); QSizePolicy vertical_splitter_policy=this->p_vertical_splitter->sizePolicy(); vertical_splitter_policy.setHorizontalStretch(4); vertical_splitter_policy.setVerticalStretch(100); this->p_vertical_splitter->setSizePolicy(vertical_splitter_policy); QSizePolicy key_table_policy=this->p_key_table->sizePolicy(); key_table_policy.setVerticalStretch(5); key_table_policy.setHorizontalStretch(100); this->p_key_table->setSizePolicy(key_table_policy); QSizePolicy tab_widget_policy=this->p_tab_widget->sizePolicy(); tab_widget_policy.setVerticalStretch(2); tab_widget_policy.setHorizontalStretch(200); this->p_tab_widget->setSizePolicy(tab_widget_policy); // Connect signals this->connect(this->p_node_tree, SIGNAL(clicked(QModelIndex)), this, SLOT(SlotNodeTreeClicked(QModelIndex))); this->connect(this->p_node_tree, SIGNAL(activated(QModelIndex)), this, SLOT(SlotNodeTreeClicked(QModelIndex))); this->connect(this->p_node_tree, SIGNAL(CurrentItemChanged(QModelIndex)), this, SLOT(SlotNodeTreeClicked(QModelIndex))); this->connect(this->p_key_table, SIGNAL(clicked(QModelIndex)), this, SLOT(SlotKeyTableClicked(QModelIndex))); this->connect(this->p_key_table, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(SlotKeyTableDoubleClicked(QModelIndex))); this->connect(this->p_key_table, SIGNAL(CurrentItemChanged(QModelIndex)), this, SLOT(SlotKeyTableClicked(QModelIndex))); this->connect(this->p_tab_widget, SIGNAL(tabCloseRequested(int)), this, SLOT(SlotTabCloseButtonClicked(int))); this->connect(this->p_node_tree, SIGNAL(SignalAddNode(QModelIndex)), this, SLOT(SlotAddNode(QModelIndex))); this->connect(this->p_node_tree, SIGNAL(SignalDeleteNode(QModelIndex)), this, SLOT(SlotDeleteNode(QModelIndex))); // Add central widget this->setCentralWidget(this->p_horizontal_splitter); this->centralWidget()->setContentsMargins(4,4,4,0); // Set window title this->UpdateWindowTitle(); // Create and update recently opened menu this->p_recently_opened_menu=new QMenu(this); this->ui->ActionRecentlyOpened->setMenu(this->p_recently_opened_menu); this->UpdateRecentlyOpenedMenu(); // Update EnableWriteSupport menu according to defaults this->UpdateEnableWriteSupportMenu(); // Load report templates this->p_reports=new Reports(this->p_settings); // Finally, react on some command line arguments if(this->p_args->IsSet("maximized")) { this->setWindowState(Qt::WindowMaximized); } if(this->p_args->IsSet("fullscreen")) { this->setWindowState(Qt::WindowFullScreen); } if(this->p_args->IsSet("hive-file")) { this->OpenHive(this->p_args->GetArgVal("hive-file")); } } +/* + * Destructor + */ MainWindow::~MainWindow() { if(this->is_hive_open) { this->p_hive->Close(); } // Delete created objects delete this->p_reports; this->ClearRecentlyOpenedMenu(); delete this->p_recently_opened_menu; delete this->p_hex_edit_widget; delete this->p_tab_widget; delete this->p_key_table; delete this->p_vertical_splitter; delete this->p_node_tree; delete this->p_horizontal_splitter; delete this->p_settings; delete this->p_hive; delete ui; } /******************************************************************************* * Protected ******************************************************************************/ +/* + * closeEvent + */ void MainWindow::closeEvent(QCloseEvent *p_event) { Q_UNUSED(p_event) + // Make sure the user can save any changes + // TODO: If saving fails, let the user cancel closing + this->SaveHiveChanges(); + // Save window position and size on exit this->p_settings->SetWindowGeometry("MainWindow",this->saveGeometry()); QMainWindow::closeEvent(p_event); } /******************************************************************************* * Private slots ******************************************************************************/ +/* + * on_action_Open_hive_triggered + */ void MainWindow::on_action_Open_hive_triggered() { QString hive_file=""; hive_file=QFileDialog::getOpenFileName(this, tr("Open registry hive"), this->p_settings->GetLastOpenLocation(), tr("All files (*)")); if(hive_file=="") return; this->OpenHive(hive_file); } +/* + * on_action_Close_hive_triggered + */ void MainWindow::on_action_Close_hive_triggered() { + // Make sure the user can save any changes + // TODO: If saving fails, let the user cancel closing + this->SaveHiveChanges(); + if(this->is_hive_open) { // Remove search results while(this->p_tab_widget->count()>1) { this->p_tab_widget->removeTab(this->p_tab_widget->count()-1); delete this->search_result_widgets.at(this->p_tab_widget->count()-1); this->search_result_widgets.removeLast(); } // Delete models if(this->p_reg_node_tree_model!=NULL) { this->p_node_tree->setModel(NULL); delete this->p_reg_node_tree_model_proxy; delete this->p_reg_node_tree_model; this->p_reg_node_tree_model_proxy=NULL; this->p_reg_node_tree_model=NULL; } if(this->p_reg_key_table_model!=NULL) { this->p_key_table->setModel(NULL); delete this->p_reg_key_table_model; this->p_reg_key_table_model=NULL; } // Remove any data from hex edit and data interpreter this->p_hex_edit_widget->SetData(QByteArray()); this->p_hex_edit_widget->setEnabled(false); // Close hive this->p_hive->Close(); this->is_hive_open=false; this->is_hive_writable=!this->p_settings->GetOpenHivesReadOnly(); this->UpdateWindowTitle(); this->UpdateMenuStates(); this->UpdateEnableWriteSupportMenu(); } } +/* + * on_action_Quit_triggered + */ void MainWindow::on_action_Quit_triggered() { qApp->exit(); } +/* + * on_ActionSearch_triggered + */ void MainWindow::on_ActionSearch_triggered() { DlgSearch dlg_search(this); if(dlg_search.exec()==QDialog::Accepted) { // Create search thread and connect needed signals/slots this->p_search_thread=new ThreadSearch(this); // Add new search widget to tabwidget and to internal widget list SearchResultWidget *p_search_widget= new SearchResultWidget(this->p_tab_widget); p_search_widget->setEnabled(false); this->search_result_widgets.append(p_search_widget); this->connect(p_search_widget, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(SlotSearchResultWidgetDoubleClicked(QModelIndex))); this->p_tab_widget->addTab(p_search_widget,tr("Search results"),true); this->p_tab_widget->setCurrentIndex(this->p_tab_widget->count()-1); // Connect search thread to result widget this->connect(this->p_search_thread, SIGNAL(SignalFoundMatch(ThreadSearch::eMatchType, QString,QString,QString)), p_search_widget, SLOT(SlotFoundMatch(ThreadSearch::eMatchType, QString,QString,QString))); this->connect(this->p_search_thread, SIGNAL(finished()), this, SLOT(SlotSearchFinished())); this->connect(this->p_search_thread, SIGNAL(finished()), p_search_widget, SLOT(SlotSearchFinished())); // Start searching this->ui->ActionSearch->setEnabled(false); p_search_thread->Search(this->p_hive->Filename(), dlg_search.Keywords(), dlg_search.SearchNodeNames(), dlg_search.SearchKeyNames(), dlg_search.SearchKeyValues()); } } +/* + * on_ActionEnableWriteSupport_triggered + */ void MainWindow::on_ActionEnableWriteSupport_triggered() { - if(this->is_hive_writable && this->p_hive->HasChangesToCommit()) { - // There are unsaved changes, ask user if we should commit them - if(QMessageBox::warning(this, - tr("Save changes"), - tr("You have unsaved changes! Do you want to save them before switching to read-only mode?"), - QMessageBox::Yes, - QMessageBox::No)==QMessageBox::Yes) - { - if(!this->p_hive->CommitChanges()) { - QMessageBox::critical(this, - tr("Saving changes"), - tr("Unable to save changes!")); - } - } - } + // There might be unsaved changes, give the user the chance to save them + // TODO: If saving failed, give the user the chance to cancel switching + this->SaveHiveChanges(); + + // Switch write support this->is_hive_writable=!this->is_hive_writable; this->UpdateEnableWriteSupportMenu(); this->p_node_tree->SetWritable(this->is_hive_writable); this->p_key_table->SetWritable(this->is_hive_writable); this->UpdateWindowTitle(this->p_hive->Filename()); } +/* + * on_ActionPreferences_triggered + */ void MainWindow::on_ActionPreferences_triggered() { DlgPreferences dlg_preferences(this->p_settings,this); dlg_preferences.exec(); // Update vars, objects and GUI elements which might be affected by the new // settings this->UpdateRecentlyOpenedMenu(); this->is_hive_writable=!this->p_settings->GetOpenHivesReadOnly(); this->UpdateEnableWriteSupportMenu(); this->p_reports->LoadReportTemplates(); } +/* + * on_ActionGenerateReport_triggered + */ void MainWindow::on_ActionGenerateReport_triggered() { DlgReportChooser dlg_repchooser(this->p_reports, this->p_hive->HiveTypeToString( this->p_hive->HiveType()), this->p_settings, this); if(dlg_repchooser.exec()==QDialog::Accepted) { QList selected_reports; // Get selected report selected_reports=dlg_repchooser.GetSelectedReports(); if(selected_reports.isEmpty()) return; // Generate report(s) QString report_result=""; if(this->p_reports->GenerateReport(this->p_hive, selected_reports, report_result, false)) { // Report generation was successfull, show reports DlgReportViewer *p_dlg_report_view=new DlgReportViewer(report_result, this->p_settings, this); p_dlg_report_view->exec(); delete p_dlg_report_view; } else { // TODO: Inform user qDebug()<<"ERROR: "<p_reports->LoadReportTemplates(); } +/* + * on_actionAbout_Qt_triggered + */ void MainWindow::on_actionAbout_Qt_triggered() { QMessageBox::aboutQt(this,tr("About Qt")); } +/* + * on_actionAbout_fred_triggered + */ void MainWindow::on_actionAbout_fred_triggered() { DlgAbout dlg_about(this); dlg_about.exec(); } +/* + * SlotNodeTreeClicked + */ void MainWindow::SlotNodeTreeClicked(QModelIndex index) { QString node_path; if(!index.isValid()) return; // Map proxy index to tree model index index=this->p_reg_node_tree_model_proxy->mapToSource(index); // Built node path node_path=this->p_reg_node_tree_model->GetNodePath(index); // Create table model and attach it to the table view if(this->p_reg_key_table_model!=NULL) { // If a previous model was set, delete it and clear hexedit etc... this->p_key_table->setModel(NULL); delete this->p_reg_key_table_model; this->p_hex_edit_widget->SetData(QByteArray()); } this->p_reg_key_table_model=new RegistryKeyTableModel(this->p_hive,node_path); this->p_key_table->setModel(this->p_reg_key_table_model, this->is_hive_writable); // Set focus back to nodetree to be able to navigate with keyboard this->p_node_tree->setFocus(); } +/* + * SlotKeyTableClicked + */ void MainWindow::SlotKeyTableClicked(QModelIndex index) { if(!index.isValid()) return; this->selected_key_value= this->p_reg_key_table_model->data(this->p_reg_key_table_model-> index(index.row(),2), RegistryKeyTableModel:: AdditionalRoles_GetRawData) .toByteArray(); this->p_hex_edit_widget->SetData(this->selected_key_value); // Set focus back to nodetree to be able to navigate with keyboard this->p_key_table->setFocus(); } +/* + * SlotKeyTableDoubleClicked + */ void MainWindow::SlotKeyTableDoubleClicked(QModelIndex index) { Q_UNUSED(index); /* QModelIndex key_index; QModelIndex node_index; QStringList nodes; QString key_name; QString key_type; QByteArray key_value; if(!index.isValid()) return; // Get key name, type and value key_index=this->p_reg_key_table_model->index(index.row(),0); key_name=this->p_reg_key_table_model->data(key_index,Qt::DisplayRole) .toString(); key_index=this->p_reg_key_table_model->index(index.row(),1); key_type=this->p_reg_key_table_model->data(key_index,Qt::DisplayRole) .toString();ThreadSearch key_index=this->p_reg_key_table_model->index(index.row(),2); key_value=this->p_reg_key_table_model->data(key_index, RegistryKeyTableModel:: AdditionalRoles_GetRawData) .toByteArray(); // Get current node node_index=this->p_node_tree->currentIndex(); //Built node path nodes.clear(); nodes.append(this->p_reg_node_tree_model-> data(node_index,Qt::DisplayRole).toString()); while(this->p_reg_node_tree_model->parent(node_index)!=QModelIndex()) { // Prepend all parent nodes node_index=this->p_reg_node_tree_model->parent(node_index); nodes.prepend(this->p_reg_node_tree_model-> data(node_index,Qt::DisplayRole).toString()); } DlgKeyDetails dlg_key_details(this); dlg_key_details.SetValues(nodes,key_name,key_type,key_value); dlg_key_details.exec(); */ } +/* + * SlotSearchFinished + */ void MainWindow::SlotSearchFinished() { delete this->p_search_thread; this->p_search_thread=NULL; this->ui->ActionSearch->setEnabled(true); // Enable result widget this->search_result_widgets.last()->setEnabled(true); } +/* + * SlotSearchResultWidgetDoubleClicked + */ void MainWindow::SlotSearchResultWidgetDoubleClicked(QModelIndex index) { SearchResultWidget *p_sender; QString path; QString match_type; QString value; QString key=""; int i; if(!index.isValid()) return; // Get pointer to sender p_sender=(SearchResultWidget*)QObject::sender(); // Get path and matchtype path=p_sender->item(index.row(),0)->text(); match_type=p_sender->item(index.row(),1)->text(); value=p_sender->item(index.row(),2)->text(); if(match_type==tr("Node name")) { // Node name is not part of path. Add it if(path=="\\") path.append(value); else path.append("\\").append(value); } else if(match_type==tr("Key name")) { // Key name is stored in value key=value; } else if(match_type==tr("Key value")) { // Key name is part of path. Save and remove it QStringList nodes=path.split("\\",QString::SkipEmptyParts); key=nodes.at(nodes.count()-1); // Remove \ from path path.chop(key.length()+1); } // Expand treeview to correct node QList indexes= this->p_reg_node_tree_model->GetIndexListOf(path); for(i=0;ip_reg_node_tree_model_proxy-> mapFromSource(indexes.at(i))); this->p_node_tree->expand(indexes.at(i)); } if(indexes.count()>0) { // Scroll to last expanded node, select it and update widgets this->p_node_tree->scrollTo(indexes.at(indexes.count()-1), QAbstractItemView::PositionAtCenter); this->p_node_tree->selectionModel()->clear(); this->p_node_tree->selectionModel()-> select(indexes.at(indexes.count()-1), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows | QItemSelectionModel::Current); // TODO: This does not work!! this->SlotNodeTreeClicked(indexes.at(indexes.count()-1)); } // Select correct key if search matched on keay name / value if(key!="") { int row=this->p_reg_key_table_model->GetKeyRow(key); this->p_key_table->clearSelection(); this->p_key_table->scrollTo(this->p_reg_key_table_model->index(row,0), QAbstractItemView::PositionAtCenter); this->p_key_table->selectRow(row); this->SlotKeyTableClicked(this->p_reg_key_table_model->index(row,0)); } } +/* + * SlotTabCloseButtonClicked + */ void MainWindow::SlotTabCloseButtonClicked(int index) { // Delete tab widget and remove tab this->p_tab_widget->removeTab(index); delete this->search_result_widgets.at(index-1); this->search_result_widgets.removeAt(index-1); } +/* + * SlotRecentlyOpenedFileClicked + */ void MainWindow::SlotRecentlyOpenedFileClicked(bool checked) { Q_UNUSED(checked) QAction *p_sender=(QAction*)QObject::sender(); this->OpenHive(p_sender->text()); } +/* + * SlotAddNode + */ void MainWindow::SlotAddNode(QModelIndex index) { QString node_path; int new_node_id; if(!index.isValid()) return; // Map proxy index to tree model index and get node path index=this->p_reg_node_tree_model_proxy->mapToSource(index); node_path=this->p_reg_node_tree_model->GetNodePath(index); // Query user for a node name bool ok=false; QString node_name=QInputDialog::getText(this, tr("Add node"), tr("Please specify a name for the new node"), QLineEdit::Normal, QString(), &ok); if(ok) { if((new_node_id=this->p_hive->AddNode(node_path,node_name))==0) { // TODO: Get error message and display it QMessageBox::critical(this, tr("Error"), tr("Unable to create node '%1\\%2'!") .arg(node_path,node_name)); } else { // Add node to model. We have to pass node_name as Ascii as utf8 names are // not supported inside hives! QModelIndex new_node_index= this->p_reg_node_tree_model->AddNode(this->p_hive, index, new_node_id, node_name.toAscii()); // Now that node has been added, expand parent and select new node this->p_node_tree->expand( this->p_reg_node_tree_model_proxy->mapFromSource(index)); new_node_index= this->p_reg_node_tree_model_proxy->mapFromSource(new_node_index); this->p_node_tree->scrollTo(new_node_index, QAbstractItemView::PositionAtCenter); this->p_node_tree->selectionModel()->clear(); this->p_node_tree->selectionModel()-> select(new_node_index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows | QItemSelectionModel::Current); // And finally update key table this->SlotNodeTreeClicked(new_node_index); } } } +/* + * SlotDeleteNode + */ void MainWindow::SlotDeleteNode(QModelIndex index) { - // TODO + QString node_path; + + if(!index.isValid()) return; + + // Map proxy index to tree model index and get node path + index=this->p_reg_node_tree_model_proxy->mapToSource(index); + node_path=this->p_reg_node_tree_model->GetNodePath(index); + + if(QMessageBox::warning(this, + tr("Delete node"), + tr("Are you sure you want to remove the node '%1' and all of its child nodes?").arg(node_path), + QMessageBox::Yes, + QMessageBox::No)==QMessageBox::Yes) + { + // Remove node from hive + if(!this->p_hive->DeleteNode(node_path)) { + // TODO: Get error message and display it + QMessageBox::critical(this, + tr("Error"), + tr("Unable to delete node '%1'!") + .arg(node_path)); + return; + } + + // Remove node from tree model and select nearest node + QModelIndex next_node_index=this->p_reg_node_tree_model->RemoveNode(index); + next_node_index= + this->p_reg_node_tree_model_proxy->mapFromSource(next_node_index); + this->p_node_tree->selectionModel()->clear(); + this->p_node_tree->selectionModel()-> + select(next_node_index, + QItemSelectionModel::ClearAndSelect | + QItemSelectionModel::Rows | + QItemSelectionModel::Current); + // And finally update key table + this->SlotNodeTreeClicked(next_node_index); + } } /******************************************************************************* * Private ******************************************************************************/ +/* + * OpenHive + */ void MainWindow::OpenHive(QString hive_file) { // Update last open location this->p_settings->SetLastOpenLocation( hive_file.left(hive_file.lastIndexOf(QDir::separator()))); // If another hive is currently open, close it if(this->is_hive_open) this->on_action_Close_hive_triggered(); // Try to open hive if(!this->p_hive->Open(hive_file,!this->is_hive_writable)) { QMessageBox::critical(this, tr("Error opening hive file"), tr("Unable to open file '%1'").arg(hive_file)); return; } // Create tree model & proxy this->p_reg_node_tree_model=new RegistryNodeTreeModel(this->p_hive); this->p_reg_node_tree_model_proxy=new RegistryNodeTreeModelProxy(this); //this->p_reg_node_tree_model_proxy->setDynamicSortFilter(true); this->p_reg_node_tree_model_proxy-> setSourceModel(this->p_reg_node_tree_model); this->p_node_tree->setModel(this->p_reg_node_tree_model_proxy, this->is_hive_writable); this->is_hive_open=true; // Enable data interpreter this->p_hex_edit_widget->setEnabled(true); // Update window title this->UpdateWindowTitle(hive_file); // Update menu states this->UpdateMenuStates(); // Add file to recent list and update recently opened menu this->p_settings->AddRecentFile(hive_file); this->UpdateRecentlyOpenedMenu(); } +/* + * UpdateWindowTitle + */ void MainWindow::UpdateWindowTitle(QString filename) { if(filename=="") { this->setWindowTitle(QString("%1 v%2").arg(APP_TITLE,APP_VERSION)); } else { this->setWindowTitle(QString("%1 v%2 - %3").arg(APP_TITLE, APP_VERSION, filename.toLocal8Bit() .constData())); if(!this->is_hive_writable) { this->setWindowTitle(this->windowTitle().append(QString(" (%1)") .arg(tr("read-only")))); } } } +/* + * UpdateMenuStates + */ void MainWindow::UpdateMenuStates() { if(this->is_hive_open) { this->ui->action_Close_hive->setEnabled(true); this->ui->ActionEnableWriteSupport->setEnabled(true); this->ui->ActionSearch->setEnabled(true); this->ui->ActionEnableWriteSupport->setEnabled(true); this->ui->ActionGenerateReport->setEnabled(true); this->ui->ActionReloadReportTemplates->setEnabled(true); } else { this->ui->action_Close_hive->setEnabled(false); this->ui->ActionEnableWriteSupport->setEnabled(false); this->ui->ActionSearch->setEnabled(false); this->ui->ActionEnableWriteSupport->setEnabled(false); this->ui->ActionGenerateReport->setEnabled(false); this->ui->ActionReloadReportTemplates->setEnabled(false); } } +/* + * ClearRecentlyOpenedMenu + */ void MainWindow::ClearRecentlyOpenedMenu() { QAction *p_action; // Remove existing menu entries QList menu_entries=this->p_recently_opened_menu->actions(); QListIterator it_me(menu_entries); while(it_me.hasNext()) { p_action=it_me.next(); this->p_recently_opened_menu->removeAction(p_action); delete p_action; } } +/* + * UpdateRecentlyOpenedMenu + */ void MainWindow::UpdateRecentlyOpenedMenu() { QStringList recent_files=this->p_settings->GetRecentFiles(); QAction *p_action; // Remove existing menu entries this->ClearRecentlyOpenedMenu(); // If there are no recent files, disable submenu and return if(recent_files.isEmpty()) { this->ui->ActionRecentlyOpened->setEnabled(false); return; } else { this->ui->ActionRecentlyOpened->setEnabled(true); } // Add recently opened files to menu QListIterator it_rf(recent_files); while(it_rf.hasNext()) { // Create menu entry p_action=new QAction(it_rf.next(),this->p_recently_opened_menu); // Connect it to us this->connect(p_action, SIGNAL(triggered(bool)), this, SLOT(SlotRecentlyOpenedFileClicked(bool))); // Add it to submenu this->p_recently_opened_menu->addAction(p_action); } } +/* + * UpdateEnableWriteSupportMenu + */ void MainWindow::UpdateEnableWriteSupportMenu() { if(!this->is_hive_writable) { this->ui->ActionEnableWriteSupport->setText(tr("Enable &write support")); this->p_node_tree->SetWritable(false); this->p_key_table->SetWritable(false); } else { this->ui->ActionEnableWriteSupport->setText(tr("Disable &write support")); this->p_node_tree->SetWritable(true); this->p_key_table->SetWritable(true); } } + +/* + * SaveHiveChanges + */ +bool MainWindow::SaveHiveChanges() { + if(!this->is_hive_open) return true; + if(!this->is_hive_writable) return true; + if(!this->p_hive->HasChangesToCommit()) return true; + + // There are unsaved changes, ask user if we should commit them + if(QMessageBox::warning(this, + tr("Save changes"), + tr("If you don't save your changes now, they will be lost. Do you want to save them?"), + QMessageBox::Yes, + QMessageBox::No)==QMessageBox::Yes) + { + if(!this->p_hive->CommitChanges()) { + // TODO: Get error message + QMessageBox::critical(this, + tr("Saving changes"), + tr("Unable to save changes!")); + return false; + } + } + return true; +} diff --git a/trunk/mainwindow.h b/trunk/mainwindow.h index dd4d832..45321f3 100644 --- a/trunk/mainwindow.h +++ b/trunk/mainwindow.h @@ -1,123 +1,124 @@ /******************************************************************************* * fred Copyright (c) 2011-2013 by Gillen Daniel * * * * Forensic Registry EDitor (fred) is a cross-platform M$ registry hive editor * * with special feautures useful during forensic analysis. * * * * This program is free software: you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the Free * * Software Foundation, either version 3 of the License, or (at your option) * * any later version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT * * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * * more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * *******************************************************************************/ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include #include #include #include #include #include #include #include #include #include "argparser.h" #include "registryhive.h" #include "registrynodetree.h" #include "registrynodetreemodel.h" #include "registrynodetreemodelproxy.h" #include "registrykeytable.h" #include "registrykeytablemodel.h" #include "hexeditwidget.h" #include "reports.h" #include "threadsearch.h" #include "searchresultwidget.h" #include "tabwidget.h" #include "settings.h" namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(ArgParser *p_arg_parser); ~MainWindow(); protected: void closeEvent(QCloseEvent *p_event); private slots: void on_action_Open_hive_triggered(); void on_action_Close_hive_triggered(); void on_action_Quit_triggered(); void on_ActionSearch_triggered(); void on_ActionEnableWriteSupport_triggered(); void on_ActionPreferences_triggered(); void on_ActionGenerateReport_triggered(); void on_ActionReloadReportTemplates_triggered(); void on_actionAbout_Qt_triggered(); void on_actionAbout_fred_triggered(); void SlotNodeTreeClicked(QModelIndex index); void SlotKeyTableClicked(QModelIndex index); void SlotKeyTableDoubleClicked(QModelIndex index); void SlotSearchFinished(); void SlotSearchResultWidgetDoubleClicked(QModelIndex index); void SlotTabCloseButtonClicked(int index); void SlotRecentlyOpenedFileClicked(bool checked); void SlotAddNode(QModelIndex index); void SlotDeleteNode(QModelIndex index); private: Ui::MainWindow *ui; ArgParser *p_args; RegistryHive *p_hive; bool is_hive_open; bool is_hive_writable; QByteArray selected_key_value; QList search_result_widgets; Settings *p_settings; // Models RegistryNodeTreeModel *p_reg_node_tree_model; RegistryNodeTreeModelProxy *p_reg_node_tree_model_proxy; RegistryKeyTableModel *p_reg_key_table_model; // Widgets etc... RegistryNodeTree *p_node_tree; RegistryKeyTable *p_key_table; TabWidget *p_tab_widget; HexEditWidget *p_hex_edit_widget; QSplitter *p_horizontal_splitter; QSplitter *p_vertical_splitter; Reports *p_reports; QMenu *p_recently_opened_menu; // Threads ThreadSearch *p_search_thread; // Functions void OpenHive(QString hive_file); void UpdateWindowTitle(QString filename=""); void UpdateMenuStates(); void ClearRecentlyOpenedMenu(); void UpdateRecentlyOpenedMenu(); void UpdateEnableWriteSupportMenu(); + bool SaveHiveChanges(); }; #endif // MAINWINDOW_H diff --git a/trunk/registrynode.cpp b/trunk/registrynode.cpp index 87b6621..33fcc97 100644 --- a/trunk/registrynode.cpp +++ b/trunk/registrynode.cpp @@ -1,65 +1,74 @@ /******************************************************************************* * fred Copyright (c) 2011-2013 by Gillen Daniel * * * * Forensic Registry EDitor (fred) is a cross-platform M$ registry hive editor * * with special feautures useful during forensic analysis. * * * * This program is free software: you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the Free * * Software Foundation, either version 3 of the License, or (at your option) * * any later version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT * * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * * more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * *******************************************************************************/ #include "registrynode.h" RegistryNode::RegistryNode(const QList &data, RegistryNode *p_parent) { this->node_data=data; this->p_parent_node=p_parent; } RegistryNode::~RegistryNode() { qDeleteAll(this->child_nodes); } void RegistryNode::AppendChild(RegistryNode *p_child) { this->child_nodes.append(p_child); } +void RegistryNode::RemoveChild(uint64_t row) { + if(row>=this->child_nodes.count()) return; + + // Remove child from list and delete it (Will also delete all sub-nodes) + RegistryNode *p_child; + p_child=this->child_nodes.takeAt(row); + delete p_child; +} + RegistryNode* RegistryNode::Child(uint64_t row) { return this->child_nodes.value(row); } uint64_t RegistryNode::ChildCount() const { return this->child_nodes.count(); } QVariant RegistryNode::Data(int column) const { if(column>=0 && column<2) { return this->node_data.value(column); } else { return QVariant(); } } uint64_t RegistryNode::Row() const { if(this->p_parent_node) { return this->p_parent_node-> child_nodes.indexOf(const_cast(this)); } else { return 0; } } RegistryNode *RegistryNode::Parent() { return this->p_parent_node; } diff --git a/trunk/registrynode.h b/trunk/registrynode.h index 9becba9..127dac9 100644 --- a/trunk/registrynode.h +++ b/trunk/registrynode.h @@ -1,48 +1,49 @@ /******************************************************************************* * fred Copyright (c) 2011-2013 by Gillen Daniel * * * * Forensic Registry EDitor (fred) is a cross-platform M$ registry hive editor * * with special feautures useful during forensic analysis. * * * * This program is free software: you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the Free * * Software Foundation, either version 3 of the License, or (at your option) * * any later version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT * * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * * more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * *******************************************************************************/ #ifndef REGISTRYNODE_H #define REGISTRYNODE_H #include #include #include class RegistryNode { public: RegistryNode(const QList &data, RegistryNode *p_parent=0); ~RegistryNode(); void AppendChild(RegistryNode *p_child); + void RemoveChild(uint64_t row); RegistryNode *Child(uint64_t row); uint64_t ChildCount() const; QVariant Data(int column) const; uint64_t Row() const; RegistryNode *Parent(); private: QList child_nodes; QList node_data; RegistryNode *p_parent_node; }; #endif // REGISTRYNODE_H diff --git a/trunk/registrynodetree.cpp b/trunk/registrynodetree.cpp index f7cf24d..cb7692b 100644 --- a/trunk/registrynodetree.cpp +++ b/trunk/registrynodetree.cpp @@ -1,211 +1,181 @@ /******************************************************************************* * fred Copyright (c) 2011-2013 by Gillen Daniel * * * * Forensic Registry EDitor (fred) is a cross-platform M$ registry hive editor * * with special feautures useful during forensic analysis. * * * * This program is free software: you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the Free * * Software Foundation, either version 3 of the License, or (at your option) * * any later version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT * * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * * more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * *******************************************************************************/ #include "registrynodetree.h" #include "registrynodetreemodel.h" #include #include #include /******************************************************************************* * Public ******************************************************************************/ RegistryNodeTree::RegistryNodeTree(QWidget *p_parent) : QTreeView(p_parent) { this->is_writable=false; // Configure widget this->setTextElideMode(Qt::ElideNone); this->setSelectionMode(QAbstractItemView::SingleSelection); this->setSelectionBehavior(QAbstractItemView::SelectRows); this->sortByColumn(0,Qt::AscendingOrder); this->setSortingEnabled(true); // Create context menu items this->p_action_add_node=new QAction(tr("Add node"),this); - this->p_action_rename_node=new QAction(tr("Rename node"),this); this->p_action_delete_node=new QAction(tr("Delete node"),this); this->p_menu_copy=new QMenu(tr("Copy"),this); this->p_action_copy_node_name=new QAction(tr("Node name"),this->p_menu_copy); this->p_menu_copy->addAction(this->p_action_copy_node_name); this->p_action_copy_node_path=new QAction(tr("Node path"),this->p_menu_copy); this->p_menu_copy->addAction(this->p_action_copy_node_path); // Connect context menu signals this->connect(this->p_action_add_node, SIGNAL(triggered()), this, SLOT(SlotAddNode())); - this->connect(this->p_action_rename_node, - SIGNAL(triggered()), - this, - SLOT(SlotRenameNode())); this->connect(this->p_action_delete_node, SIGNAL(triggered()), this, SLOT(SlotDeleteNode())); this->connect(this->p_action_copy_node_name, SIGNAL(triggered()), this, SLOT(SlotCopyNodeName())); this->connect(this->p_action_copy_node_path, SIGNAL(triggered()), this, SLOT(SlotCopyNodePath())); } RegistryNodeTree::~RegistryNodeTree() { // Delete context menu delete this->p_action_copy_node_name; delete this->p_action_copy_node_path; delete this->p_menu_copy; delete this->p_action_delete_node; - delete this->p_action_rename_node; delete this->p_action_add_node; } void RegistryNodeTree::setModel(QAbstractItemModel *p_model, bool writable) { // Assign model to view QTreeView::setModel(p_model); this->header()->setResizeMode(0,QHeaderView::ResizeToContents); this->header()->setStretchLastSection(true); if(p_model!=NULL && p_model->index(0,0).isValid()) { // Select first tree item this->setCurrentIndex(p_model->index(0,0)); } // Set writable status this->SetWritable(writable); } void RegistryNodeTree::SetWritable(bool writable) { this->is_writable=writable; this->p_action_add_node->setEnabled(this->is_writable); - this->p_action_rename_node->setEnabled(this->is_writable); this->p_action_delete_node->setEnabled(this->is_writable); } /******************************************************************************* * Protected ******************************************************************************/ void RegistryNodeTree::contextMenuEvent(QContextMenuEvent *p_event) { // Only show context menu when a node is selected if(this->selectedIndexes().count()!=2) return; // Only show context menu when user clicked on selected row // TODO: Does not work when clicking on column 2 if(this->indexAt(p_event->pos())!=this->selectedIndexes().at(0)) return; // Emit a click signal emit(this->clicked(this->indexAt(p_event->pos()))); // Create context menu and add actions QMenu context_menu(this); context_menu.addAction(this->p_action_add_node); - context_menu.addAction(this->p_action_rename_node); context_menu.addAction(this->p_action_delete_node); context_menu.addSeparator(); context_menu.addMenu(this->p_menu_copy); context_menu.exec(p_event->globalPos()); } void RegistryNodeTree::keyPressEvent(QKeyEvent *p_event) { // Only react if a node is selected and user pressed Key_Left if(this->selectedIndexes().count()==2 && p_event->key()==Qt::Key_Left) { QModelIndex cur_index=this->selectedIndexes().at(0); if(this->model()->hasChildren(cur_index) && this->isExpanded(cur_index)) { // Current node is expanded. Only collapse this one this->collapse(cur_index); return; } if(!cur_index.parent().isValid()) { // Do no try to collapse anything above root node return; } this->collapse(cur_index.parent()); this->setCurrentIndex(cur_index.parent()); return; } // If we didn't handle the key event, let our parent handle it QTreeView::keyPressEvent(p_event); } void RegistryNodeTree::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) { // Call parent class's currentChanged first QTreeView::currentChanged(current,previous); // Now emit our signal QModelIndex current_item=QModelIndex(current); emit(RegistryNodeTree::CurrentItemChanged(current_item)); } /******************************************************************************* * Private slots ******************************************************************************/ +void RegistryNodeTree::SlotAddNode() { + emit(RegistryNodeTree::SignalAddNode(this->selectedIndexes().at(0))); +} + +void RegistryNodeTree::SlotDeleteNode() { + emit(RegistryNodeTree::SignalDeleteNode(this->selectedIndexes().at(0))); +} + void RegistryNodeTree::SlotCopyNodeName() { QApplication::clipboard()-> setText(this->selectedIndexes().at(0).data().toString(), QClipboard::Clipboard); } void RegistryNodeTree::SlotCopyNodePath() { QString path=((RegistryNodeTreeModel*)(this->model()))-> GetNodePath(this->selectedIndexes().at(0)); QApplication::clipboard()->setText(path,QClipboard::Clipboard); } - -void RegistryNodeTree::SlotAddNode() { - emit(RegistryNodeTree::SignalAddNode(this->selectedIndexes().at(0))); -} - -void RegistryNodeTree::SlotRenameNode() { - emit(RegistryNodeTree::SignalRenameNode(this->selectedIndexes().at(0))); -/* - // Get current node name and path - QString node_name=this->selectedIndexes().at(0).data().toString(); - QString node_path=((RegistryNodeTreeModel*)(this->model()))-> - GetNodePath(this->selectedIndexes().at(0)); - - // Query new name - bool ok=false; - QString new_node_name=QInputDialog::getText(this, - tr("Rename node"), - tr("Please specify a new name for the node"), - QLineEdit::Normal, - node_name, - &ok); - if(ok) { - - } -*/ -} - -void RegistryNodeTree::SlotDeleteNode() { - emit(RegistryNodeTree::SignalDeleteNode(this->selectedIndexes().at(0))); -} diff --git a/trunk/registrynodetree.h b/trunk/registrynodetree.h index a8a92c6..6a7d988 100644 --- a/trunk/registrynodetree.h +++ b/trunk/registrynodetree.h @@ -1,70 +1,68 @@ /******************************************************************************* * fred Copyright (c) 2011-2013 by Gillen Daniel * * * * Forensic Registry EDitor (fred) is a cross-platform M$ registry hive editor * * with special feautures useful during forensic analysis. * * * * This program is free software: you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the Free * * Software Foundation, either version 3 of the License, or (at your option) * * any later version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT * * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * * more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * *******************************************************************************/ #ifndef REGISTRYNODETREE_H #define REGISTRYNODETREE_H #include #include #include #include #include class RegistryNodeTree : public QTreeView { Q_OBJECT public: RegistryNodeTree(QWidget *p_parent=0); ~RegistryNodeTree(); void setModel(QAbstractItemModel *p_model, bool writable=false); void SetWritable(bool writable); Q_SIGNALS: void CurrentItemChanged(QModelIndex current); void SignalAddNode(QModelIndex root_node); void SignalRenameNode(QModelIndex node); void SignalDeleteNode(QModelIndex node); protected: - // int sizeHintForColumn(int column) const; +// int sizeHintForColumn(int column) const; void contextMenuEvent(QContextMenuEvent *p_event); void keyPressEvent(QKeyEvent *p_event); void currentChanged(const QModelIndex ¤t, const QModelIndex &previous); private: bool is_writable; QAction *p_action_add_node; - QAction *p_action_rename_node; QAction *p_action_delete_node; QMenu *p_menu_copy; QAction *p_action_copy_node_name; QAction *p_action_copy_node_path; private slots: void SlotAddNode(); - void SlotRenameNode(); void SlotDeleteNode(); void SlotCopyNodeName(); void SlotCopyNodePath(); }; #endif // REGISTRYNODETREE_H diff --git a/trunk/registrynodetreemodel.cpp b/trunk/registrynodetreemodel.cpp index 3083277..236f4bc 100644 --- a/trunk/registrynodetreemodel.cpp +++ b/trunk/registrynodetreemodel.cpp @@ -1,268 +1,311 @@ /******************************************************************************* * fred Copyright (c) 2011-2013 by Gillen Daniel * * * * Forensic Registry EDitor (fred) is a cross-platform M$ registry hive editor * * with special feautures useful during forensic analysis. * * * * This program is free software: you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the Free * * Software Foundation, either version 3 of the License, or (at your option) * * any later version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT * * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * * more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * *******************************************************************************/ #include "registrynodetreemodel.h" #include #include #include #include #include /******************************************************************************* * Public ******************************************************************************/ RegistryNodeTreeModel::RegistryNodeTreeModel(RegistryHive *p_hive, QObject *p_parent) : QAbstractItemModel(p_parent) { // Create root node. It's values will be used as header values. this->p_root_node=new RegistryNode(QList()<SetupModelData(p_hive,this->p_root_node); } RegistryNodeTreeModel::~RegistryNodeTreeModel() { delete this->p_root_node; } QVariant RegistryNodeTreeModel::data(const QModelIndex &index, int role) const { if(!index.isValid()) return QVariant(); if(role!=Qt::DisplayRole) return QVariant(); RegistryNode *p_node=static_cast(index.internalPointer()); switch(role) { case Qt::DisplayRole: { switch(index.column()) { case RegistryNodeTreeModel::ColumnContent_NodeName: { return p_node->Data(index.column()); break; } case RegistryNodeTreeModel::ColumnContent_NodeModTime: { QDateTime date_time; bool ok=false; date_time.setTimeSpec(Qt::UTC); date_time.setTime_t(RegistryHive::FiletimeToUnixtime( p_node->Data(index.column()).toLongLong(&ok))); if(ok) return date_time.toString("yyyy/MM/dd hh:mm:ss"); else return tr("Unknown"); break; } default: { return QVariant(); } } break; } default: { return QVariant(); } } // Control will never reach this, but in order to stop g++ complaining... return QVariant(); } Qt::ItemFlags RegistryNodeTreeModel::flags(const QModelIndex &index) const { if(!index.isValid()) return 0; return Qt::ItemIsEnabled | Qt::ItemIsSelectable; } QVariant RegistryNodeTreeModel::headerData(int section, Qt::Orientation orientation, int role) const { // Only horizontal header is supported if(orientation!=Qt::Horizontal) return QVariant(); switch(role) { case Qt::TextAlignmentRole: // Handle text alignment return Qt::AlignCenter; break; case Qt::DisplayRole: // Header text return this->p_root_node->Data(section); break; default: return QVariant(); } } QModelIndex RegistryNodeTreeModel::index(int row, int column, const QModelIndex &parent) const { if(!this->hasIndex(row,column,parent)) return QModelIndex(); RegistryNode *p_parent_node; if(!parent.isValid()) { p_parent_node=this->p_root_node; } else { p_parent_node=static_cast(parent.internalPointer()); } RegistryNode *p_child_node=p_parent_node->Child(row); if(p_child_node) { return this->createIndex(row,column,p_child_node); } else { return QModelIndex(); } } QModelIndex RegistryNodeTreeModel::parent(const QModelIndex &index) const { if(!index.isValid()) return QModelIndex(); RegistryNode *p_child_node= static_cast(index.internalPointer()); RegistryNode *p_parent_node=p_child_node->Parent(); if(p_parent_node==this->p_root_node) { return QModelIndex(); } else { return this->createIndex(p_parent_node->Row(),0,p_parent_node); } } int RegistryNodeTreeModel::rowCount(const QModelIndex &parent) const { if(parent.column()>0) return 0; RegistryNode *p_parent_node; if(!parent.isValid()) { p_parent_node=this->p_root_node; } else { p_parent_node=static_cast(parent.internalPointer()); } return p_parent_node->ChildCount(); } int RegistryNodeTreeModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 2; } QList RegistryNodeTreeModel::GetIndexListOf(QString path) const { RegistryNode *p_parent_node=this->p_root_node; QList ret; QStringList nodes=path.split("\\",QString::SkipEmptyParts); bool found=false; // Create a list of all index's that form the supplied path ret.clear(); for(int i=0;iChildCount();ii++) { if(p_parent_node->Child(ii)->Data(0)==nodes.at(i)) { ret.append(this->createIndex(ii,0,p_parent_node->Child(ii))); p_parent_node=p_parent_node->Child(ii); found=true; break; } } // Break when last child node was found if(found && i==nodes.count()-1) break; // Return an empty list when a child node wasn't found else if(!found) return QList(); } return ret; } QString RegistryNodeTreeModel::GetNodePath(QModelIndex child_index) const { QString path; // Get current node name path=this->data(child_index,Qt::DisplayRole).toString().prepend("\\"); // Build node path by prepending all parent nodes names while(this->parent(child_index)!=QModelIndex()) { child_index=this->parent(child_index); path.prepend(this->data(child_index, Qt::DisplayRole).toString().prepend("\\")); } return path; } QModelIndex RegistryNodeTreeModel::AddNode(RegistryHive *p_hive, const QModelIndex &parent_index, int new_node_id, QString new_node_name) { RegistryNode *p_parent_node; int64_t key_mod_time; RegistryNode *p_node; // Get pointer to parent node p_parent_node=static_cast(parent_index.internalPointer()); // Tell users of this view that we are going to insert a row emit(RegistryNodeTreeModel::beginInsertRows(parent_index, p_parent_node->ChildCount(), p_parent_node->ChildCount())); // Create and add new node in internal node list key_mod_time=p_hive->GetNodeModTime(new_node_id); p_node=new RegistryNode(QList()<AppendChild(p_node); // Tell users of this view we have finished inserting a row emit(RegistryNodeTreeModel::endInsertRows()); // Return index to new node return this->createIndex(p_parent_node->ChildCount()-1,0,p_node); } +QModelIndex RegistryNodeTreeModel::RemoveNode(const QModelIndex &index) { + RegistryNode *p_node; + RegistryNode *p_parent_node; + int node_row; + QModelIndex parent_node_index; + + // Get pointers to current node and its parent + p_node=static_cast(index.internalPointer()); + p_parent_node=p_node->Parent(); + + // Get current nodes row + node_row=p_node->Row(); + + // Create index of parent node + parent_node_index=this->createIndex(p_parent_node->Row(),0,p_parent_node); + + // Tell users of this view that we are going to remove a row + emit(RegistryNodeTreeModel::beginRemoveRows(parent_node_index, + node_row, + node_row)); + + // Remove node + p_parent_node->RemoveChild(node_row); + + // Tell users of this view we have finished removing a row + emit(RegistryNodeTreeModel::endRemoveRows()); + + // Find a suitable index that should be selected after the current one has + // been deleted. + if(p_parent_node->ChildCount()>0) { + // Parent node still has child nodes, return nearest child node + if(node_rowChildCount()) { + // Any child node removed except the last one, row is still valid + return this->createIndex(node_row,0,p_parent_node->Child(node_row)); + } else { + // Last child node removed, row-1 should be valid + return this->createIndex(node_row-1,0,p_parent_node->Child(node_row-1)); + } + } + // If no child nodes are left, return parent node + return parent_node_index; +} + /******************************************************************************* * Private ******************************************************************************/ void RegistryNodeTreeModel::SetupModelData(RegistryHive *p_hive, RegistryNode *p_parent, int hive_node) { QMap hive_children; RegistryNode *p_node; int64_t key_mod_time; // Get all sub nodes of current hive node if(hive_node) hive_children=p_hive->GetNodes(hive_node); else hive_children=p_hive->GetNodes("\\"); if(hive_children.isEmpty()) return; // Recursivly iterate over all sub nodes QMapIterator i(hive_children); while(i.hasNext()) { i.next(); key_mod_time=p_hive->GetNodeModTime(i.value()); // TODO: Maybe we have to call GetErrorMsg in case an error occured p_node=new RegistryNode(QList()<AppendChild(p_node); this->SetupModelData(p_hive,p_node,i.value()); } } diff --git a/trunk/registrynodetreemodel.h b/trunk/registrynodetreemodel.h index f9f9305..17d8f12 100644 --- a/trunk/registrynodetreemodel.h +++ b/trunk/registrynodetreemodel.h @@ -1,69 +1,70 @@ /******************************************************************************* * fred Copyright (c) 2011-2013 by Gillen Daniel * * * * Forensic Registry EDitor (fred) is a cross-platform M$ registry hive editor * * with special feautures useful during forensic analysis. * * * * This program is free software: you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the Free * * Software Foundation, either version 3 of the License, or (at your option) * * any later version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT * * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * * more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * *******************************************************************************/ #ifndef REGISTRYNODETREEMODEL_H #define REGISTRYNODETREEMODEL_H #include #include #include #include "registrynode.h" #include "registryhive.h" class RegistryNodeTreeModel : public QAbstractItemModel { Q_OBJECT public: RegistryNodeTreeModel(RegistryHive *p_hive, QObject *p_parent=0); ~RegistryNodeTreeModel(); QVariant data(const QModelIndex &index, int role) const; Qt::ItemFlags flags(const QModelIndex &index) const; QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const; QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const; QModelIndex parent(const QModelIndex &index) const; int rowCount(const QModelIndex &parent=QModelIndex()) const; int columnCount(const QModelIndex &parent=QModelIndex()) const; QList GetIndexListOf(QString path) const; QString GetNodePath(QModelIndex child_index) const; QModelIndex AddNode(RegistryHive *p_hive, const QModelIndex &parent_index, int new_node_id, QString new_node_name); + QModelIndex RemoveNode(const QModelIndex &index); private: enum ColumnContent { ColumnContent_NodeName=0, ColumnContent_NodeModTime }; RegistryNode *p_root_node; void SetupModelData(RegistryHive *p_hive, RegistryNode *p_parent, int hive_node=0); }; #endif // REGISTRYNODETREEMODEL_H