mirror of
https://gitlab.com/apparmor/apparmor
synced 2025-09-05 00:35:13 +00:00
1157 lines
33 KiB
C++
1157 lines
33 KiB
C++
/* AppArmor Profile Editor (C) 2006 Novell, Inc.
|
|
*
|
|
* 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 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
*/
|
|
|
|
// TODO:
|
|
// * Find box in profile list
|
|
// * Go through and make sure that translation will work correctly
|
|
|
|
#include "wx/wxprec.h"
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/wx.h"
|
|
#endif
|
|
|
|
#include <wx/treectrl.h> // For profileTree
|
|
#include <wx/dir.h> // Various functions
|
|
#include <wx/filedlg.h> // Save As dialog
|
|
#include <wx/cmdline.h> // For launching apps
|
|
#include <wx/html/helpctrl.h> // The help window
|
|
#include <wx/fs_zip.h> // Needed by the help window
|
|
#include <wx/process.h> // For forking off the profile reload process
|
|
#include <wx/wfstream.h> // Used by the profile reload function
|
|
#include <wx/fdrepdlg.h> // Find/Replace dialog
|
|
|
|
#include "ProfileTextCtrl.h"
|
|
#include "AboutDialog.h"
|
|
#include "profileeditor.h"
|
|
#include "ProfileDirectoryTraverser.h"
|
|
#include "Preferences.h"
|
|
#include "SearchAllProfiles.h"
|
|
#include "Configuration.h"
|
|
|
|
BEGIN_EVENT_TABLE( ProfileToolFrame, wxFrame )
|
|
EVT_END_PROCESS(ID_RELOAD_PROFILE_PROCESS, ProfileToolFrame::OnEndOfProfileReload)
|
|
EVT_FIND(wxID_ANY, ProfileToolFrame::OnFindButton)
|
|
EVT_FIND_NEXT(wxID_ANY, ProfileToolFrame::OnFindButton)
|
|
EVT_FIND_REPLACE(wxID_ANY, ProfileToolFrame::OnFindButton)
|
|
EVT_FIND_REPLACE_ALL(wxID_ANY, ProfileToolFrame::OnFindButton)
|
|
EVT_FIND_CLOSE(wxID_ANY, ProfileToolFrame::OnFindButton)
|
|
EVT_MENU(wxID_EXIT, ProfileToolFrame::OnQuit)
|
|
EVT_MENU(ID_MENU_FILE_NEW, ProfileToolFrame::OnNewProfile)
|
|
EVT_MENU(ID_MENU_FILE_PREFERENCES, ProfileToolFrame::OnPreferences)
|
|
EVT_MENU(ID_MENU_HELP_ABOUT, ProfileToolFrame::OnAbout)
|
|
EVT_MENU(ID_MENU_FILE_SAVE, ProfileToolFrame::OnSave)
|
|
EVT_MENU(ID_MENU_FILE_SAVE_AS, ProfileToolFrame::OnSaveAs)
|
|
EVT_MENU(ID_MENU_HELP_PROFILES, ProfileToolFrame::OnHelp)
|
|
EVT_MENU(ID_MENU_EDIT_COPY, ProfileToolFrame::OnEditMenu)
|
|
EVT_MENU(ID_MENU_EDIT_CUT, ProfileToolFrame::OnEditMenu)
|
|
EVT_MENU(ID_MENU_EDIT_PASTE, ProfileToolFrame::OnEditMenu)
|
|
EVT_MENU(ID_MENU_EDIT_UNDO, ProfileToolFrame::OnEditMenu)
|
|
EVT_MENU(ID_MENU_EDIT_REDO, ProfileToolFrame::OnEditMenu)
|
|
EVT_MENU(ID_MENU_EDIT_FIND, ProfileToolFrame::OnFind)
|
|
EVT_MENU(ID_MENU_EDIT_REPLACE, ProfileToolFrame::OnReplace)
|
|
EVT_MENU(ID_MENU_EDIT_SEARCH_ALL_PROFILES, ProfileToolFrame::OnSearchAllProfiles)
|
|
EVT_MENU(ID_TREE_CONTEXT_DELETE, ProfileToolFrame::OnDeleteProfile)
|
|
EVT_MENU(ID_TREE_CONTEXT_RELOAD, ProfileToolFrame::OnReloadProfile)
|
|
EVT_MENU(wxSTC_APPARMOR_OPEN_INCLUDE_MENU_ITEM, ProfileToolFrame::OnOpenInclude)
|
|
EVT_MENU(wxSTC_APPARMOR_INSERT_INCLUDE_MENU_ITEM, ProfileToolFrame::OnInsertInclude)
|
|
EVT_MENU(wxSTC_APPARMOR_SEARCH_PHRASE_IN_ALL_PROFILES, ProfileToolFrame::OnSearchAllProfiles)
|
|
EVT_TREE_ITEM_ACTIVATED(ID_PROFILE_TREE, ProfileToolFrame::OnTreeSelection)
|
|
EVT_TREE_ITEM_MENU(ID_PROFILE_TREE, ProfileToolFrame::OnTreeContextMenu)
|
|
EVT_STC_CHANGE(ID_STYLED_PROFILE_WINDOW, ProfileToolFrame::OnProfileModified)
|
|
EVT_STC_UPDATEUI(ID_STYLED_PROFILE_WINDOW, ProfileToolFrame::OnUpdateUI)
|
|
EVT_SIZE(ProfileToolFrame::OnSize)
|
|
EVT_CLOSE(ProfileToolFrame::OnClose)
|
|
END_EVENT_TABLE()
|
|
|
|
IMPLEMENT_APP(AppArmorProfileTool)
|
|
|
|
/**
|
|
* The standard wxApp OnInit function
|
|
* @return Success or fail
|
|
*/
|
|
bool AppArmorProfileTool::OnInit()
|
|
{
|
|
wxApp::SetAppName(_T("AppArmorProfileEditor"));
|
|
wxString startingProfile = wxEmptyString;
|
|
wxString startingHighlight = wxEmptyString;
|
|
|
|
// Set up the command line parser
|
|
// usage: profileeditor [profile to open] [phrase to highlight]
|
|
wxCmdLineEntryDesc commandLineDesc[3];
|
|
commandLineDesc[0].kind = wxCMD_LINE_PARAM;
|
|
commandLineDesc[0].shortName = NULL;
|
|
commandLineDesc[0].longName = NULL;
|
|
commandLineDesc[0].description = _("Path to profile");
|
|
commandLineDesc[0].type = wxCMD_LINE_VAL_STRING;
|
|
commandLineDesc[0].flags = wxCMD_LINE_PARAM_OPTIONAL;
|
|
commandLineDesc[1].kind = wxCMD_LINE_PARAM;
|
|
commandLineDesc[1].shortName = NULL;
|
|
commandLineDesc[1].longName = NULL;
|
|
commandLineDesc[1].description = _("Search phrase to highlight");
|
|
commandLineDesc[1].type = wxCMD_LINE_VAL_STRING;
|
|
commandLineDesc[1].flags = wxCMD_LINE_PARAM_OPTIONAL;
|
|
commandLineDesc[2].kind = wxCMD_LINE_NONE; // End the list
|
|
|
|
// See if they gave us a profile to load
|
|
wxCmdLineParser parser(commandLineDesc, argc, argv);
|
|
int parserReturn = parser.Parse(true);
|
|
|
|
if (0 == parserReturn) // Successful parsing
|
|
{
|
|
int results = parser.GetParamCount();
|
|
if (results >= 1)
|
|
startingProfile = parser.GetParam(0);
|
|
|
|
if (results >= 2)
|
|
startingHighlight = parser.GetParam(1);
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// No sense in letting them any further if they aren't root
|
|
if (geteuid() != 0) {
|
|
wxMessageDialog *dlg = new wxMessageDialog(NULL,
|
|
_T("You must be root to run this program."), _T("Error"),
|
|
wxOK|wxICON_ERROR);
|
|
dlg->ShowModal();
|
|
dlg->Destroy();
|
|
return FALSE;
|
|
}
|
|
|
|
frame = new ProfileToolFrame(_("AppArmor Profile Editor" ),
|
|
wxPoint(50,50),
|
|
wxSize(640,480),
|
|
startingProfile,
|
|
startingHighlight);
|
|
frame->Show(TRUE);
|
|
SetTopWindow(frame);
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* This isn't actually used
|
|
* @return
|
|
*/
|
|
int AppArmorProfileTool::OnExit()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
ProfileToolFrame::ProfileToolFrame(const wxString& title,
|
|
const wxPoint& pos,
|
|
const wxSize& size,
|
|
const wxString &startingProfile,
|
|
const wxString &startingHighlight)
|
|
: wxFrame((wxFrame *) NULL, -1, title, pos, size)
|
|
{
|
|
mpProfileTree = NULL;
|
|
mpFindDialog = NULL;
|
|
mpReplaceDialog = NULL;
|
|
mpProfileView = NULL;
|
|
mpMenuBar = NULL;
|
|
|
|
|
|
wxFileSystem::AddHandler(new wxZipFSHandler); // Needed by the help handler
|
|
if (wxFileExists(_(HELP_FILE_LOCATION)))
|
|
{
|
|
mpHelpController = new wxHtmlHelpController;
|
|
mpHelpController->Initialize(_(HELP_FILE_LOCATION));
|
|
}
|
|
else
|
|
{
|
|
mpHelpController = NULL;
|
|
}
|
|
|
|
// I have no idea why "search up" is the default,
|
|
// but I suspect most people will expect otherwise
|
|
mFindData.SetFlags(wxFR_DOWN);
|
|
mReplaceData.SetFlags(wxFR_DOWN);
|
|
CreateControls(startingProfile, startingHighlight);
|
|
}
|
|
|
|
ProfileToolFrame::~ProfileToolFrame()
|
|
{
|
|
delete mpHelpController;
|
|
delete wxConfig::Get();
|
|
}
|
|
|
|
/**
|
|
* The great big nasty function that puts the main frame together.
|
|
*
|
|
* @param startingProfile The initial profile to open
|
|
* @param startingHighlight A phrase to highlight
|
|
*/
|
|
void ProfileToolFrame::CreateControls(const wxString &startingProfile, const wxString &startingHighlight)
|
|
{
|
|
Configuration::Initialize();
|
|
// Set up our menu
|
|
wxMenu* menuFile = new wxMenu;
|
|
wxMenu* menuEdit = new wxMenu;
|
|
wxMenu* menuHelp = new wxMenu;
|
|
CreateStatusBar();
|
|
menuFile->Append(ID_MENU_FILE_NEW, _("&New Profile\tCtrl-N"));
|
|
menuFile->Append(ID_MENU_FILE_SAVE, _("&Save Profile\tCtrl-S"));
|
|
menuFile->Append(ID_MENU_FILE_SAVE_AS, _("Save Profile As..."));
|
|
menuFile->Append(ID_MENU_FILE_PREFERENCES, _("&Preferences...\tCtrl-P"));
|
|
menuFile->AppendSeparator();
|
|
menuFile->Append(wxID_EXIT, _("E&xit\tCtrl-Q"));
|
|
menuEdit->Append(ID_MENU_EDIT_UNDO, _("&Undo\tCtrl-Z"));
|
|
menuEdit->Append(ID_MENU_EDIT_REDO, _("&Redo\tShift-Ctrl-Z"));
|
|
menuEdit->AppendSeparator();
|
|
menuEdit->Append(ID_MENU_EDIT_CUT, _("C&ut\tCtrl-X"));
|
|
menuEdit->Append(ID_MENU_EDIT_COPY,_("C&opy\tCtrl-C"));
|
|
menuEdit->Append(ID_MENU_EDIT_PASTE, _("P&aste\tCtrl-V"));
|
|
menuEdit->AppendSeparator();
|
|
menuEdit->Append(ID_MENU_EDIT_FIND, _("&Find\tCtrl-F"));
|
|
menuEdit->Append(ID_MENU_EDIT_FIND_NEXT, _("Fin&d Next\tF3"));
|
|
menuEdit->Append(ID_MENU_EDIT_REPLACE, _("R&eplace\tCtrl-H"));
|
|
menuEdit->AppendSeparator();
|
|
menuEdit->Append(ID_MENU_EDIT_SEARCH_ALL_PROFILES, _("&Search all profiles\tAlt-Ctrl-F"));
|
|
menuHelp->Append(ID_MENU_HELP_ABOUT, _("A&bout"));
|
|
menuHelp->Append(ID_MENU_HELP_PROFILES, _("P&rofiles\tF1"));
|
|
mpMenuBar = new wxMenuBar;
|
|
mpMenuBar->Append(menuFile, _("&File"));
|
|
mpMenuBar->Append(menuEdit, _("&Edit"));
|
|
mpMenuBar->Append(menuHelp, _("&Help"));
|
|
|
|
mpMenuBar->Enable(ID_MENU_FILE_SAVE, false);
|
|
mpMenuBar->Enable(ID_MENU_FILE_SAVE_AS, false);
|
|
mpMenuBar->Enable(ID_MENU_EDIT_UNDO, false);
|
|
mpMenuBar->Enable(ID_MENU_EDIT_REDO, false);
|
|
mpMenuBar->Enable(ID_MENU_EDIT_FIND_NEXT, false);
|
|
SetMenuBar( mpMenuBar );
|
|
|
|
// Restore the user's window position if there was one
|
|
Move(Configuration::GetWindowPos());
|
|
SetSize(Configuration::GetWindowSize());
|
|
|
|
mpSplitterWindow = new wxSplitterWindow(this,
|
|
wxID_ANY,
|
|
wxDefaultPosition,
|
|
GetClientSize(), wxSP_3D| wxEXPAND);
|
|
|
|
// The tree control displays the list of profiles.
|
|
mpProfileTree = new wxTreeCtrl(mpSplitterWindow, ID_PROFILE_TREE, wxDefaultPosition, wxDefaultSize,
|
|
wxTR_HAS_BUTTONS
|
|
| wxTR_SINGLE);
|
|
mpProfileView = new ProfileTextCtrl (mpSplitterWindow,
|
|
ID_STYLED_PROFILE_WINDOW,
|
|
wxDefaultPosition,
|
|
wxDefaultSize);
|
|
mpSplitterWindow->SplitVertically(mpProfileTree, mpProfileView, 200);
|
|
mpSplitterWindow->SetMinimumPaneSize(20);
|
|
|
|
mRootNode = mpProfileTree->AddRoot(_("Profiles"));
|
|
mCurrentNode.Unset();
|
|
PopulateControls(); // Load up the profile tree
|
|
|
|
mpProfileView->SetFileName(_T(""));
|
|
mpProfileView->SetIsNew(true);
|
|
SetStatusText(_("New Profile"));
|
|
SetTitle(_("New Profile"));
|
|
|
|
if ((wxEmptyString != startingProfile) && wxFileExists(startingProfile))
|
|
{
|
|
LoadProfile(startingProfile, startingHighlight);
|
|
FindTreeNodeAndHighlight(mRootNode, startingProfile);
|
|
}
|
|
|
|
mpMenuBar->Enable(ID_MENU_EDIT_PASTE, mpProfileView->CanPaste());
|
|
}
|
|
|
|
/**
|
|
* Clears the current text window
|
|
*/
|
|
void ProfileToolFrame::ClearProfile()
|
|
{
|
|
mpMenuBar->Enable(ID_MENU_FILE_SAVE, false);
|
|
mpMenuBar->Enable(ID_MENU_FILE_SAVE_AS, false);
|
|
mpMenuBar->Enable(ID_MENU_EDIT_UNDO, false);
|
|
mpMenuBar->Enable(ID_MENU_EDIT_REDO, false);
|
|
SetStatusText(_("New profile"));
|
|
SetTitle(_("New Profile"));
|
|
mpProfileView->SetIgnoreChanges(true);
|
|
mpProfileView->SetFileName(_(""));
|
|
mpProfileView->ClearAll();
|
|
mpProfileView->EmptyUndoBuffer();
|
|
mpProfileView->SetText(Configuration::GetTemplateText());
|
|
mpProfileView->SetIsNew(true); // It's a new profile
|
|
mpProfileView->SetNeedSaving(false);
|
|
if (mCurrentNode.IsOk())
|
|
mpProfileTree->SetItemTextColour(mCurrentNode, *wxBLACK);
|
|
mpProfileView->SetIgnoreChanges(false);
|
|
}
|
|
|
|
/**
|
|
* Deletes a profile from disk.
|
|
*
|
|
* @param profile
|
|
* @return Successful delete or not
|
|
*/
|
|
bool ProfileToolFrame::DeleteProfile(wxString &profile)
|
|
{
|
|
return wxRemoveFile(profile);
|
|
}
|
|
|
|
/**
|
|
* Given a path to a profile, find it in the tree, and highlight it.
|
|
* This is only used when a profile is passed via argv.
|
|
* @param startingNode The node to start at
|
|
* @param path Path to the profile
|
|
* @return 0 on success, -1 on failure
|
|
*/
|
|
int ProfileToolFrame::FindTreeNodeAndHighlight(wxTreeItemId startingNode, wxString path)
|
|
{
|
|
wxTreeItemIdValue cookie;
|
|
wxTreeItemId search;
|
|
wxTreeItemId node = mpProfileTree->GetFirstChild(startingNode, cookie );
|
|
wxTreeItemId child;
|
|
ProfileTreeData *data;
|
|
|
|
while(node.IsOk())
|
|
{
|
|
if (mpProfileTree->ItemHasChildren(node))
|
|
{
|
|
if (0 == FindTreeNodeAndHighlight(node, path))
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
data = (ProfileTreeData *) mpProfileTree->GetItemData(node);
|
|
if (NULL != data)
|
|
{
|
|
if (data->GetPath() == path)
|
|
{
|
|
// Whee, found it. Highlight it
|
|
if (mCurrentNode.IsOk())
|
|
mpProfileTree->SetItemTextColour(mCurrentNode, *wxBLACK);
|
|
mCurrentNode = node;
|
|
mpProfileTree->SetItemTextColour(mCurrentNode, *wxBLUE);
|
|
mpProfileTree->SelectItem(mCurrentNode);
|
|
mpProfileTree->EnsureVisible(mCurrentNode);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
node = mpProfileTree->GetNextChild(startingNode, cookie);
|
|
}
|
|
|
|
return -1; // Couldn't find it
|
|
}
|
|
|
|
/**
|
|
* Load a profile from disk into the profile view
|
|
* @param profile Profile to load
|
|
* @param highlight Text to highlight
|
|
*/
|
|
void ProfileToolFrame::LoadProfile (const wxString& profile, const wxString& highlight)
|
|
{
|
|
|
|
if (mpProfileView->LoadFile(profile))
|
|
{
|
|
// Select the first instance of a phrase. This is used primarily
|
|
// when a user runs a "search all profiles"
|
|
if (wxEmptyString != highlight)
|
|
{
|
|
int searchResult = mpProfileView->FindText(0,
|
|
mpProfileView->GetTextLength(),
|
|
highlight,
|
|
0 | 0);
|
|
if (searchResult >= 0)
|
|
{
|
|
mpProfileView->SetCurrentPos(searchResult + highlight.Length());
|
|
mpProfileView->SetSelection(searchResult, searchResult + highlight.Length());
|
|
}
|
|
|
|
}
|
|
|
|
SetStatusText(profile);
|
|
SetTitle(profile);
|
|
|
|
// Reset the save menu items until we're notified of a modification
|
|
mpMenuBar->Enable(ID_MENU_FILE_SAVE, false);
|
|
mpMenuBar->Enable(ID_MENU_FILE_SAVE_AS, false);
|
|
mpMenuBar->Enable(ID_MENU_EDIT_UNDO, false);
|
|
mpMenuBar->Enable(ID_MENU_EDIT_REDO, false);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Scans through the default profile directory and loads the names into
|
|
* the profile tree.
|
|
*/
|
|
void ProfileToolFrame::PopulateControls()
|
|
{
|
|
// Scan the directory structure
|
|
ProfileDirectoryTraverser traverser (mpProfileTree, mRootNode, Configuration::GetProfileDirectory());
|
|
wxDir dir (Configuration::GetProfileDirectory());
|
|
dir.Traverse(traverser, wxEmptyString);
|
|
mpProfileTree->SortChildren(mRootNode);
|
|
mpProfileTree->Expand(mRootNode);
|
|
}
|
|
|
|
/**
|
|
* Called when text in the profile view has been changed, enabling the Save(As) menu functions
|
|
*/
|
|
void ProfileToolFrame::ProfileHasBeenModified()
|
|
{
|
|
if (!mpProfileView->GetIsNew()) // Don't let them "Save" unless we have a filename
|
|
mpMenuBar->Enable(ID_MENU_FILE_SAVE, true);
|
|
mpMenuBar->Enable(ID_MENU_FILE_SAVE_AS, true);
|
|
mpMenuBar->Enable(ID_MENU_EDIT_UNDO, mpProfileView->CanUndo());
|
|
mpMenuBar->Enable(ID_MENU_EDIT_REDO, mpProfileView->CanRedo());
|
|
if ((mCurrentNode != mRootNode) && mCurrentNode.IsOk())
|
|
mpProfileTree->SetItemTextColour(mCurrentNode, *wxRED);
|
|
mpProfileView->SetNeedSaving(true);
|
|
}
|
|
|
|
/**
|
|
* Asks the user whether or not they'd like to save the profile
|
|
* @return -1 on "Cancel", 0 on a successful save, 1 on "No"
|
|
*/
|
|
int ProfileToolFrame::ProfileNeedSaving ()
|
|
{
|
|
int ret = -1;
|
|
switch (wxMessageBox(_("Would you like to save your active profile?"),
|
|
_("Save Profile"),
|
|
wxICON_QUESTION | wxYES_NO | wxCANCEL))
|
|
{
|
|
case wxCANCEL:
|
|
ret = -1;
|
|
break;
|
|
case wxYES:
|
|
ret = SaveCurrentProfile(); // This will either be 0 for successful save
|
|
// or -1 for a failure
|
|
break;
|
|
case wxNO:
|
|
ret = 1;
|
|
break;
|
|
default:
|
|
ret = -1;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* Feeds the profile to the AppArmor parser.
|
|
* @param filePath
|
|
*/
|
|
void ProfileToolFrame::ReloadProfile (const wxString &filePath)
|
|
{
|
|
wxString parserCommand = Configuration::GetParserCommand();
|
|
parserCommand.Append(_(" -r "));
|
|
wxProcess *proc = new wxProcess(this, ID_RELOAD_PROFILE_PROCESS);
|
|
proc->Redirect();
|
|
wxExecute(parserCommand, wxEXEC_ASYNC, proc);
|
|
wxFileInputStream inputStream (filePath);
|
|
inputStream.Read(*proc->GetOutputStream());
|
|
proc->GetOutputStream()->Close();
|
|
}
|
|
|
|
/**
|
|
* Clears the tree and reloads the profile tree. Used when someone modifies the
|
|
* default profile directory in the preferences.
|
|
*/
|
|
void ProfileToolFrame::RepopulateControl()
|
|
{
|
|
mCurrentNode = mRootNode;
|
|
mpProfileTree->CollapseAndReset(mRootNode);
|
|
PopulateControls();
|
|
}
|
|
|
|
/**
|
|
* Replaces all instances of a phrase, without user confirmation
|
|
* @param searchText Text to search for
|
|
* @param replaceText Replacement text
|
|
* @param matchCase
|
|
* @param wholeWord
|
|
*/
|
|
void ProfileToolFrame::ReplaceAll (const wxString searchText, const wxString replaceText, int matchCase, int wholeWord)
|
|
{
|
|
int startResult, i;
|
|
int len = searchText.Length();
|
|
i = 0;
|
|
startResult = mpProfileView->FindText(0, mpProfileView->GetTextLength(),
|
|
searchText,
|
|
matchCase | wholeWord);
|
|
while (-1 != startResult)
|
|
{
|
|
mpProfileView->SetCurrentPos(startResult + len);
|
|
mpProfileView->SetSelection(startResult, startResult +len);
|
|
|
|
mpProfileView->ReplaceSelection(replaceText);
|
|
startResult = mpProfileView->FindText(mpProfileView->GetCurrentPos(),
|
|
mpProfileView->GetTextLength(),
|
|
searchText,
|
|
matchCase | wholeWord);
|
|
i++;
|
|
}
|
|
|
|
mpProfileView->SetNeedSaving(true);
|
|
wxString informMessage;
|
|
informMessage.Printf(_("Made %i replacements."), i);
|
|
wxMessageDialog *dlg = new wxMessageDialog(this,
|
|
informMessage,
|
|
_("Finished"),
|
|
wxOK|wxICON_INFORMATION);
|
|
dlg->ShowModal();
|
|
dlg->Destroy();
|
|
}
|
|
|
|
/**
|
|
* A small wrapper around the profile view's save function.
|
|
* @return -1 on failure, 0 on success
|
|
*/
|
|
int ProfileToolFrame::SaveCurrentProfile ()
|
|
{
|
|
wxString filename = mpProfileView->GetFileName();
|
|
if (mpProfileView->SaveFile(filename))
|
|
{
|
|
mpProfileView->SetNeedSaving(false);
|
|
mpProfileTree->SetItemTextColour(mCurrentNode, *wxBLUE);
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
///////// Event handler functions
|
|
|
|
/**
|
|
* Event handler triggered by the Help->About menu
|
|
* @param event
|
|
*/
|
|
void ProfileToolFrame::OnAbout( wxCommandEvent& WXUNUSED(event) )
|
|
{
|
|
AboutDialog *aboutDialog = new AboutDialog(this, wxID_ANY);
|
|
aboutDialog->ShowModal();
|
|
aboutDialog->Destroy();
|
|
}
|
|
|
|
/**
|
|
* Event handler triggered by a close event
|
|
* @param WXUNUSED( event )
|
|
*/
|
|
void ProfileToolFrame::OnClose( wxCloseEvent& WXUNUSED(event) )
|
|
{
|
|
// Save our window settings
|
|
wxPoint pos = GetPosition();
|
|
wxSize size = GetSize();
|
|
Configuration::WriteWindowSettings(GetPosition(), GetSize());
|
|
if (mpProfileView->GetNeedSaving())
|
|
{
|
|
if (ProfileNeedSaving() != -1)
|
|
Destroy();
|
|
}
|
|
else
|
|
{
|
|
Destroy();
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Event handler triggered by the "Delete profile" tree context menu
|
|
* @param event
|
|
*/
|
|
void ProfileToolFrame::OnDeleteProfile(wxCommandEvent &event)
|
|
{
|
|
wxString filePath;
|
|
wxTreeItemId selectedId = mpProfileTree->GetSelection();
|
|
ProfileTreeData *data = (ProfileTreeData *) mpProfileTree->GetItemData(selectedId);
|
|
filePath = data->GetPath();
|
|
|
|
switch (wxMessageBox(_("Are you sure you want to delete this profile?"),
|
|
_("Delete Profile"),
|
|
wxICON_QUESTION | wxYES_NO))
|
|
{
|
|
case wxYES:
|
|
if (DeleteProfile(filePath))
|
|
{
|
|
// See if the profile we just deleted matches
|
|
// what's currently in the editor
|
|
if (mpProfileView->GetFileName() == filePath) // Yes, it is
|
|
{
|
|
mCurrentNode.Unset();
|
|
ClearProfile();
|
|
}
|
|
// Remove the profile from the tree
|
|
mpProfileTree->Delete(selectedId);
|
|
}
|
|
break;
|
|
case wxNO:
|
|
break;
|
|
default:
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Event handler for the Cut/Copy/Paste/Undo/Redo Edit menu events
|
|
* @param event
|
|
*/
|
|
void ProfileToolFrame::OnEditMenu(wxCommandEvent &event)
|
|
{
|
|
switch(event.GetId())
|
|
{
|
|
case ID_MENU_EDIT_COPY:
|
|
mpMenuBar->Enable(ID_MENU_EDIT_UNDO, mpProfileView->CanPaste());
|
|
mpProfileView->Copy();
|
|
break;
|
|
case ID_MENU_EDIT_CUT:
|
|
mpMenuBar->Enable(ID_MENU_EDIT_UNDO, mpProfileView->CanPaste());
|
|
mpProfileView->Cut();
|
|
break;
|
|
case ID_MENU_EDIT_PASTE:
|
|
mpProfileView->Paste();
|
|
break;
|
|
case ID_MENU_EDIT_UNDO:
|
|
mpProfileView->Undo();
|
|
mpMenuBar->Enable(ID_MENU_EDIT_UNDO, mpProfileView->CanUndo());
|
|
mpMenuBar->Enable(ID_MENU_EDIT_REDO, mpProfileView->CanRedo());
|
|
break;
|
|
case ID_MENU_EDIT_REDO:
|
|
mpProfileView->Redo();
|
|
mpMenuBar->Enable(ID_MENU_EDIT_UNDO, mpProfileView->CanUndo());
|
|
mpMenuBar->Enable(ID_MENU_EDIT_REDO, mpProfileView->CanRedo());
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Event handler triggered by the end of the profile reload child
|
|
* process. Checks the exit code and reports success or failure to the user
|
|
* @param event
|
|
*/
|
|
void ProfileToolFrame::OnEndOfProfileReload(wxProcessEvent &event)
|
|
{
|
|
wxMessageDialog *dlg;
|
|
if (0 == event.GetExitCode())
|
|
{
|
|
dlg = new wxMessageDialog(this, _("Successfully reloaded profile."), _("Success"),
|
|
wxOK|wxICON_INFORMATION);
|
|
}
|
|
else
|
|
{
|
|
dlg = new wxMessageDialog(this, _("Reload failed!"), _("Error"),
|
|
wxOK|wxICON_ERROR);
|
|
}
|
|
|
|
dlg->ShowModal();
|
|
dlg->Destroy();
|
|
|
|
}
|
|
|
|
/**
|
|
* Event handler triggered by the "Find" menu item
|
|
* @param WXUNUSED(event)
|
|
*/
|
|
void ProfileToolFrame::OnFind (wxCommandEvent& WXUNUSED(event))
|
|
{
|
|
if (mpProfileView->GetSelectedText() != _(""))
|
|
mFindData.SetFindString(mpProfileView->GetSelectedText());
|
|
mpProfileView->SetSelection(mpProfileView->GetCurrentPos(), mpProfileView->GetCurrentPos());
|
|
mpFindDialog = new wxFindReplaceDialog (this, &mFindData, _("Find"));
|
|
mpFindDialog->Show();
|
|
}
|
|
|
|
/**
|
|
* Event handler triggered by the "Find" button in the Find/Replace dialog
|
|
* @param event
|
|
*/
|
|
void ProfileToolFrame::OnFindButton (wxFindDialogEvent& event)
|
|
{
|
|
wxEventType type = event.GetEventType();
|
|
int flags = event.GetFlags();
|
|
int matchCase = (flags & wxFR_MATCHCASE) ? wxSTC_FIND_MATCHCASE : 0;
|
|
int wholeWord = (flags & wxFR_WHOLEWORD) ? wxSTC_FIND_WHOLEWORD : 0;
|
|
int startResult = 0;
|
|
int len = event.GetFindString().Length();
|
|
wxString searchText = event.GetFindString().c_str();
|
|
|
|
if ( type == wxEVT_COMMAND_FIND || type == wxEVT_COMMAND_FIND_NEXT )
|
|
{
|
|
if (flags & wxFR_DOWN)
|
|
{
|
|
startResult = mpProfileView->FindText(mpProfileView->GetCurrentPos(),
|
|
mpProfileView->GetTextLength(),
|
|
searchText,
|
|
matchCase | wholeWord);
|
|
}
|
|
else
|
|
{
|
|
mpProfileView->SearchAnchor(); // Sets the anchor to the current position
|
|
startResult = mpProfileView->SearchPrev(matchCase | wholeWord, searchText);
|
|
}
|
|
|
|
// Move the cursor and highlight the text
|
|
if (startResult >= 0)
|
|
{
|
|
mpProfileView->SetCurrentPos(startResult + len);
|
|
mpProfileView->SetSelection(startResult, startResult +len);
|
|
mpMenuBar->Enable(ID_MENU_EDIT_FIND_NEXT, true);
|
|
}
|
|
else
|
|
{
|
|
mpProfileView->SetSelection(mpProfileView->GetCurrentPos(), mpProfileView->GetCurrentPos());
|
|
mpMenuBar->Enable(ID_MENU_EDIT_FIND_NEXT, false);
|
|
wxMessageDialog *dlg = new wxMessageDialog(this,
|
|
_("Finished searching the profile"),
|
|
_("Finished"),
|
|
wxOK|wxICON_INFORMATION);
|
|
dlg->ShowModal();
|
|
dlg->Destroy();
|
|
}
|
|
}
|
|
|
|
else if (type == wxEVT_COMMAND_FIND_REPLACE)
|
|
{
|
|
if (mpProfileView->GetSelectedText() != _T(""))
|
|
{
|
|
mpProfileView->ReplaceSelection(event.GetReplaceString());
|
|
mpProfileView->SetNeedSaving(true);
|
|
}
|
|
|
|
int startResult = -1;
|
|
if (flags & wxFR_DOWN)
|
|
{
|
|
startResult = mpProfileView->FindText(mpProfileView->GetCurrentPos(),
|
|
mpProfileView->GetTextLength(),
|
|
searchText,
|
|
matchCase | wholeWord);
|
|
}
|
|
else
|
|
{
|
|
mpProfileView->SearchAnchor();
|
|
startResult = mpProfileView->SearchPrev(matchCase | wholeWord, searchText);
|
|
}
|
|
|
|
if (startResult != -1)
|
|
{
|
|
mpProfileView->SetCurrentPos(startResult + len);
|
|
mpProfileView->SetSelection(startResult, startResult + len);
|
|
}
|
|
else
|
|
{
|
|
mpProfileView->SetSelection(mpProfileView->GetCurrentPos(), mpProfileView->GetCurrentPos());
|
|
wxMessageDialog *dlg = new wxMessageDialog(this, _("Finished searching the profile"),
|
|
_("Finished"),
|
|
wxOK|wxICON_INFORMATION);
|
|
dlg->ShowModal();
|
|
dlg->Destroy();
|
|
}
|
|
}
|
|
else if (type == wxEVT_COMMAND_FIND_REPLACE_ALL )
|
|
{
|
|
ReplaceAll(event.GetFindString(), event.GetReplaceString(), matchCase, wholeWord);
|
|
wxFindReplaceDialog *dlg = event.GetDialog();
|
|
dlg->Destroy();
|
|
}
|
|
else if (type == wxEVT_COMMAND_FIND_CLOSE)
|
|
{
|
|
wxFindReplaceDialog *dlg = event.GetDialog();
|
|
dlg->Destroy();
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Event handler triggered by the Help->Profiles menu item.
|
|
* The help controller was loaded earlier, and if the help file
|
|
* was not found AT THAT TIME, it will warn the user.
|
|
* @param WXUNUSED( event )
|
|
*/
|
|
void ProfileToolFrame::OnHelp(wxCommandEvent& WXUNUSED(event))
|
|
{
|
|
if (mpHelpController != NULL)
|
|
{
|
|
mpHelpController->DisplayContents();
|
|
}
|
|
else
|
|
{
|
|
wxString message = _("The help file was not in the expected place: ");
|
|
message.Append(_(HELP_FILE_LOCATION));
|
|
wxMessageDialog *dlg = new wxMessageDialog(this, message,
|
|
_("Error"),
|
|
wxOK|wxICON_ERROR);
|
|
dlg->ShowModal();
|
|
dlg->Destroy();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Event handler triggered by the profile view's "Insert an include..."
|
|
* context menu item. It'll pop up a file selection dialog and insert the selection.
|
|
* @param WXUNUSED(event)
|
|
*/
|
|
void ProfileToolFrame::OnInsertInclude (wxCommandEvent& WXUNUSED(event))
|
|
{
|
|
wxFileDialog *includeFileDialog = new wxFileDialog(this, _("Choose files to include"),
|
|
Configuration::GetProfileDirectory(),
|
|
_T(""), _T("*.*"), wxOPEN|wxMULTIPLE);
|
|
if (includeFileDialog->ShowModal() == wxID_OK)
|
|
{
|
|
wxArrayString paths;
|
|
includeFileDialog->GetPaths(paths);
|
|
int size = paths.GetCount();
|
|
for (int i = 0; i < size; i++)
|
|
{
|
|
mpProfileView->AddText(_T("\n#include <"));
|
|
mpProfileView->AddText(paths[i]);
|
|
mpProfileView->AddText(_T(">"));
|
|
}
|
|
ProfileHasBeenModified();
|
|
}
|
|
includeFileDialog->Destroy();
|
|
}
|
|
|
|
/**
|
|
* Event handler triggered by File->New Profile
|
|
* @param WXUNUSED( event )
|
|
*/
|
|
void ProfileToolFrame::OnNewProfile(wxCommandEvent& WXUNUSED(event))
|
|
{
|
|
if (mpProfileView->GetNeedSaving())
|
|
if (-1 == ProfileNeedSaving())
|
|
return;
|
|
|
|
ClearProfile();
|
|
}
|
|
|
|
// Triggered from the editor's context menu, it'll pull the text out
|
|
// of < > brackets and open it in a new editor
|
|
/**
|
|
* Event handler triggered by the profile view's "Open Include in New..."
|
|
* context menu
|
|
* @param WXUNUSED( event )
|
|
*/
|
|
void ProfileToolFrame::OnOpenInclude(wxCommandEvent& WXUNUSED(event))
|
|
{
|
|
wxString currentLine = mpProfileView->GetCurLine();
|
|
wxString tmpString = currentLine.AfterFirst('<');
|
|
|
|
if (wxEmptyString != tmpString)
|
|
{
|
|
wxString includeString = tmpString.BeforeLast('>');
|
|
|
|
// Most #include statements that Novell ships are
|
|
// relative paths, so we'll correct that here
|
|
if (!includeString.StartsWith(_T("/")))
|
|
{
|
|
includeString.Prepend(_T("/"));
|
|
includeString.Prepend(Configuration::GetProfileDirectory());
|
|
}
|
|
|
|
includeString.Prepend(_(" "));
|
|
includeString.Prepend(Configuration::GetEditorExecutable());
|
|
wxProcess *proc = wxProcess::Open(includeString);
|
|
if (NULL == proc)
|
|
{
|
|
wxMessageDialog *dlg = new wxMessageDialog(this, _("Could not exec!"),
|
|
_("Error"),
|
|
wxOK|wxICON_ERROR);
|
|
dlg->ShowModal();
|
|
dlg->Destroy();
|
|
}
|
|
else
|
|
{
|
|
proc->Detach();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Event handler triggered by File->Preferences. Displays the preferences,
|
|
* and saves the configuration data
|
|
* @param WXUNUSED( event )
|
|
*/
|
|
void ProfileToolFrame::OnPreferences(wxCommandEvent& WXUNUSED(event))
|
|
{
|
|
PreferencesDialog *prefs = new PreferencesDialog(this,
|
|
ID_PREFERENCES_DIALOG,
|
|
_("Preferences"),
|
|
wxPoint(40,40),
|
|
wxSize(800,600));
|
|
|
|
if (prefs->ShowModal() == wxID_OK)
|
|
{
|
|
// Save the preferences
|
|
wxString newProfileDirectory = prefs->GetProfileDir();
|
|
wxString oldProfileDirectory = Configuration::GetProfileDirectory();
|
|
|
|
// Don't want to cause problems with the profile tree traverser
|
|
// down the road
|
|
if (newProfileDirectory.Last() == '/')
|
|
newProfileDirectory.RemoveLast();
|
|
|
|
Configuration::SetProfileDirectory(newProfileDirectory);
|
|
Configuration::SetParserCommand(prefs->GetParser());
|
|
Configuration::SetTemplateText(prefs->GetTemplateText());
|
|
|
|
Configuration::SetCommentColour(prefs->GetCommentButtonColour());
|
|
Configuration::SetIncludeColour(prefs->GetIncludeButtonColour());
|
|
Configuration::SetCapColour(prefs->GetCapabilityButtonColour());
|
|
Configuration::SetPathColour(prefs->GetPathButtonColour());
|
|
Configuration::SetPermColour(prefs->GetPermButtonColour());
|
|
|
|
Configuration::SetCommentFont(prefs->GetCommentButtonFont());
|
|
Configuration::SetIncludeFont(prefs->GetIncludeButtonFont());
|
|
Configuration::SetCapabilityFont(prefs->GetCapabilityButtonFont());
|
|
Configuration::SetPathFont(prefs->GetPathButtonFont());
|
|
Configuration::SetPermFont(prefs->GetPermButtonFont());
|
|
Configuration::CommitChanges();
|
|
|
|
mpProfileView->RefreshColoursAndFonts();
|
|
if (oldProfileDirectory != newProfileDirectory)
|
|
RepopulateControl();
|
|
}
|
|
prefs->Destroy();
|
|
}
|
|
|
|
/**
|
|
* Event handler passed up from the profile editor when something has changed
|
|
* in the text view.
|
|
* @param WXUNUSED( event )
|
|
*/
|
|
void ProfileToolFrame::OnProfileModified(wxStyledTextEvent& WXUNUSED(event))
|
|
{
|
|
ProfileHasBeenModified();
|
|
}
|
|
|
|
void ProfileToolFrame::OnQuit( wxCommandEvent& WXUNUSED(event) )
|
|
{
|
|
if (mpProfileView->GetNeedSaving())
|
|
{
|
|
if (ProfileNeedSaving() != -1)
|
|
{
|
|
// OnClose will be triggered next, since the two events are generated
|
|
// differently, so make sure it knows to call Destroy() immediately
|
|
mpProfileView->SetNeedSaving(false);
|
|
Close(TRUE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Close(TRUE);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Event handler triggered by the "Reload profile" tree context menu
|
|
* @param event
|
|
*/
|
|
void ProfileToolFrame::OnReloadProfile(wxCommandEvent &event)
|
|
{
|
|
wxTreeItemId selectedId = mpProfileTree->GetSelection();
|
|
ProfileTreeData *data = (ProfileTreeData *) mpProfileTree->GetItemData(selectedId);
|
|
wxString filePath = data->GetPath();
|
|
|
|
if (mpProfileView->GetNeedSaving())
|
|
if (ProfileNeedSaving() != 0)
|
|
return;
|
|
|
|
ReloadProfile(filePath);
|
|
}
|
|
|
|
/**
|
|
* Event handler triggered by Edit->Replace
|
|
* @param event
|
|
*/
|
|
void ProfileToolFrame::OnReplace (wxCommandEvent& event)
|
|
{
|
|
if (mpProfileView->GetSelectedText() != _(""))
|
|
mReplaceData.SetFindString(mpProfileView->GetSelectedText());
|
|
mpProfileView->SetSelection(mpProfileView->GetCurrentPos(), mpProfileView->GetCurrentPos());
|
|
mpReplaceDialog = new wxFindReplaceDialog (this, &mReplaceData, _("Find and Replace"), wxFR_REPLACEDIALOG);
|
|
mpReplaceDialog->Show();
|
|
}
|
|
|
|
/**
|
|
* Event handler triggered by Edit->Save
|
|
* @param WXUNUSED( event )
|
|
*/
|
|
void ProfileToolFrame::OnSave (wxCommandEvent& WXUNUSED(event))
|
|
{
|
|
// This shouldn't be triggerable if there isn't a profile, but...
|
|
if (mpProfileView->GetNeedSaving()) {
|
|
wxString filename = mpProfileView->GetFileName();
|
|
mpProfileView->SaveFile(filename);
|
|
mpProfileView->SetSavePoint();
|
|
mpMenuBar->Enable(ID_MENU_FILE_SAVE, false);
|
|
mpProfileTree->SetItemTextColour(mCurrentNode, *wxBLUE);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Event handler triggered by Edit->SaveAs
|
|
* @param WXUNUSED( event )
|
|
*/
|
|
void ProfileToolFrame::OnSaveAs (wxCommandEvent& WXUNUSED(event))
|
|
{
|
|
wxFileDialog *dialog = new wxFileDialog(this, _("Save As"),
|
|
Configuration::GetProfileDirectory(),
|
|
mpProfileView->GetFileName(), _("Any file (*)|*"),
|
|
wxSAVE| wxOVERWRITE_PROMPT);
|
|
|
|
if (dialog->ShowModal() == wxID_OK)
|
|
{
|
|
wxString newFilename = dialog->GetPath();
|
|
mpProfileView->SaveFile(newFilename);
|
|
mpProfileView->SetSavePoint();
|
|
mpProfileView->SetFileName(newFilename);
|
|
mpProfileView->SetIsNew(false);
|
|
mpProfileView->SetNeedSaving(false);
|
|
// Add it to our tree
|
|
mpProfileTree->Collapse(mRootNode);
|
|
ProfileTreeData *data = new ProfileTreeData(newFilename);
|
|
wxTreeItemId tmp_id = mpProfileTree->AppendItem(mRootNode,
|
|
wxFileName(newFilename).GetFullName(),
|
|
-1,
|
|
-1,
|
|
data);
|
|
mpProfileTree->SetItemTextColour(mCurrentNode, *wxBLACK);
|
|
mCurrentNode = tmp_id;
|
|
mpProfileTree->SetItemTextColour(mCurrentNode, *wxBLUE);
|
|
mpProfileTree->SortChildren(mRootNode);
|
|
mpProfileTree->SelectItem(tmp_id);
|
|
mpProfileTree->Expand(mRootNode);
|
|
SetStatusText(newFilename);
|
|
SetTitle(newFilename);
|
|
}
|
|
dialog->Destroy();
|
|
}
|
|
|
|
/**
|
|
* Event handler triggered by Edit->Search All
|
|
* @param event
|
|
*/
|
|
void ProfileToolFrame::OnSearchAllProfiles (wxCommandEvent& event)
|
|
{
|
|
SearchAllProfilesDialog *searchDialog = new SearchAllProfilesDialog(this);
|
|
searchDialog->SetProfileDirectory(Configuration::GetProfileDirectory());
|
|
searchDialog->SetEditorExecutable(Configuration::GetEditorExecutable());
|
|
|
|
if (wxSTC_APPARMOR_SEARCH_PHRASE_IN_ALL_PROFILES == event.GetId())
|
|
searchDialog->SetSearchText(mpProfileView->GetSelectedText());
|
|
|
|
searchDialog->ShowModal();
|
|
searchDialog->Destroy();
|
|
}
|
|
|
|
/**
|
|
* Event handler for resize events
|
|
* @param WXUNUSED( event )
|
|
*/
|
|
void ProfileToolFrame::OnSize(wxSizeEvent& WXUNUSED(event))
|
|
{
|
|
int w, h;
|
|
GetClientSize(&w, &h);
|
|
mpSplitterWindow->SetSize(0,0,w,h);
|
|
mpProfileTree->Refresh();
|
|
mpProfileTree->Update();
|
|
}
|
|
|
|
/**
|
|
* Displays a tree context menu upon right clicks
|
|
* @param event
|
|
*/
|
|
void ProfileToolFrame::OnTreeContextMenu(wxTreeEvent &event)
|
|
{
|
|
wxPoint clickPoint = event.GetPoint();
|
|
wxTreeItemId eventItem = event.GetItem();
|
|
if (eventItem.IsOk())
|
|
{
|
|
wxMenu eventMenu(_(""));
|
|
eventMenu.Append(ID_TREE_CONTEXT_RELOAD, _("&Reload this profile in AppArmor"));
|
|
eventMenu.Append(ID_TREE_CONTEXT_DELETE, _("&Delete"));
|
|
PopupMenu(&eventMenu, clickPoint);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Event handler triggered by a double click on a tree item
|
|
* @param event
|
|
*/
|
|
void ProfileToolFrame::OnTreeSelection(wxTreeEvent& event)
|
|
{
|
|
wxTreeItemId node = event.GetItem();
|
|
if (node != mRootNode)
|
|
{
|
|
wxString filePath;
|
|
ProfileTreeData *data = (ProfileTreeData *) mpProfileTree->GetItemData(node);
|
|
if (NULL == data)
|
|
{
|
|
// It's not a profile, it's a directory node
|
|
mpProfileTree->Toggle(node);
|
|
}
|
|
else
|
|
{
|
|
filePath = data->GetPath();
|
|
// Verify that there's nothing that needs to be saved
|
|
if (mpProfileView->GetNeedSaving()) // Yes, needs saving
|
|
{
|
|
if (ProfileNeedSaving() != -1) // Saved successfully
|
|
{
|
|
if (mCurrentNode.IsOk())
|
|
mpProfileTree->SetItemTextColour(mCurrentNode, *wxBLACK);
|
|
|
|
mCurrentNode = node;
|
|
mpProfileTree->SetItemTextColour(mCurrentNode,*wxBLUE);
|
|
LoadProfile(filePath, wxEmptyString);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (mCurrentNode.IsOk())
|
|
mpProfileTree->SetItemTextColour(mCurrentNode, *wxBLACK);
|
|
|
|
mCurrentNode = node;
|
|
mpProfileTree->SetItemTextColour(mCurrentNode,*wxBLUE);
|
|
LoadProfile(filePath, wxEmptyString);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Takes care of brace matching
|
|
* @param event
|
|
*/
|
|
void ProfileToolFrame::OnUpdateUI(wxStyledTextEvent& WXUNUSED(event))
|
|
{
|
|
// Clear any previous brace matches
|
|
mpProfileView->BraceHighlight (wxSTC_INVALID_POSITION, wxSTC_INVALID_POSITION);
|
|
char currentChar = mpProfileView->GetCharAt(mpProfileView->GetCurrentPos() -1 );
|
|
|
|
if ( (currentChar == '}') || (currentChar == ')') || (currentChar == ']') ) {
|
|
int currentBrace = mpProfileView->GetCurrentPos() - 1;
|
|
int matchingBrace = mpProfileView->BraceMatch(currentBrace);
|
|
|
|
if (matchingBrace != wxSTC_INVALID_POSITION)
|
|
mpProfileView->BraceHighlight(matchingBrace, currentBrace);
|
|
else
|
|
mpProfileView->BraceBadLight(currentBrace);
|
|
}
|
|
}
|
|
|