Package IDAscope :: Package idascope :: Package widgets :: Module WinApiWidget
[hide private]
[frames] | no frames]

Source Code for Module IDAscope.idascope.widgets.WinApiWidget

  1  #!/usr/bin/python 
  2  ######################################################################## 
  3  # Copyright (c) 2012 
  4  # Daniel Plohmann <daniel.plohmann<at>gmail<dot>com> 
  5  # Alexander Hanel <alexander.hanel<at>gmail<dot>com> 
  6  # All rights reserved. 
  7  ######################################################################## 
  8  # 
  9  #  This file is part of IDAscope 
 10  # 
 11  #  IDAscope is free software: you can redistribute it and/or modify it 
 12  #  under the terms of the GNU General Public License as published by 
 13  #  the Free Software Foundation, either version 3 of the License, or 
 14  #  (at your option) any later version. 
 15  # 
 16  #  This program is distributed in the hope that it will be useful, but 
 17  #  WITHOUT ANY WARRANTY; without even the implied warranty of 
 18  #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
 19  #  General Public License for more details. 
 20  # 
 21  #  You should have received a copy of the GNU General Public License 
 22  #  along with this program.  If not, see 
 23  #  <http://www.gnu.org/licenses/>. 
 24  # 
 25  ######################################################################## 
 26   
 27  from PySide import QtGui, QtCore 
 28  from PySide.QtGui import QCompleter, QLineEdit, QStringListModel, QTextBrowser, QIcon 
 29   
 30   
31 -class WinApiWidget(QtGui.QWidget):
32 """ 33 A widget for allowing easy access to Windows API information. Front-end to the I{idascope.core.WinApiProvider}. 34 """ 35
36 - def __init__(self, parent):
37 QtGui.QWidget.__init__(self) 38 print "loading WinApiWidget" 39 # enable access to shared IDAscope modules 40 self.parent = parent 41 self.name = "WinAPI Browsing" 42 self.icon = QIcon(self.parent.config.icon_file_path + "winapi.png") 43 self.search_icon = QIcon(self.parent.config.icon_file_path + "search.png") 44 self.back_icon = QIcon(self.parent.config.icon_file_path + "back.png") 45 self.forward_icon = QIcon(self.parent.config.icon_file_path + "forward.png") 46 # This widget may relay on IDAproxy 47 self.ida_proxy = self.parent.ida_proxy 48 self.winapi = self.parent.winapi_provider 49 # references to Qt-specific modules 50 self.QtGui = QtGui 51 self.QtCore = QtCore 52 self.old_keyword_initial = "" 53 self.createGui() 54 self.set_availability() 55 self.register_hotkeys()
56
57 - def set_availability(self):
58 """ 59 Adjust the availability of this widget by checking if the keyword database has been loaded. 60 If the database has not been loaded, deactivate the search functionality. 61 """ 62 if not self.winapi.has_offline_msdn_available(): 63 self.browser_window.setHtml("<font color=\"#FF0000\">Offline MSDN database is not available. To use " \ 64 + "it, have a look at the installation instructions located in the manual: " \ 65 + "IDAscope/documentation/manual.html.</font>") 66 self.search_button.setEnabled(False) 67 self.api_chooser_lineedit.setEnabled(False)
68
69 - def register_hotkeys(self):
70 """ 71 Register hotkeys with IDAscope in order to ease the use of this widget. 72 """ 73 self.parent.register_hotkey(self.parent.config.winapi_shortcut, self.navigate_to_highlighted_identifier)
74
75 - def createGui(self):
76 """ 77 Create the GUI for this widget and all of its components. 78 """ 79 self.create_back_button() 80 self.create_next_button() 81 self.create_api_chooser_lineedit() 82 self.create_search_button() 83 self.create_browser_window() 84 85 winapi_layout = QtGui.QVBoxLayout() 86 selection_widget = QtGui.QWidget() 87 selection_layout = QtGui.QHBoxLayout() 88 selection_layout.addWidget(self.back_button) 89 selection_layout.addWidget(self.next_button) 90 selection_layout.addWidget(self.api_chooser_lineedit) 91 selection_layout.addWidget(self.search_button) 92 selection_widget.setLayout(selection_layout) 93 winapi_layout.addWidget(selection_widget) 94 winapi_layout.addWidget(self.browser_window) 95 self.setLayout(winapi_layout)
96
97 - def create_back_button(self):
98 """ 99 Create a back button to allow easier browsing 100 """ 101 self.back_button = QtGui.QPushButton(self.back_icon, "", self) 102 self.back_button.setToolTip("Go back to previously accessed content.") 103 self.back_button.resize(self.back_button.sizeHint()) 104 self.back_button.setEnabled(False) 105 self.back_button.clicked.connect(self.onBackButtonClicked)
106
107 - def create_next_button(self):
108 """ 109 Create a next button to allow easier browsing 110 """ 111 self.next_button = QtGui.QPushButton(self.forward_icon, "", self) 112 self.next_button.setToolTip("Go forward to previously accessed content.") 113 self.next_button.resize(self.next_button.sizeHint()) 114 self.next_button.setEnabled(False) 115 self.next_button.clicked.connect(self.onNextButtonClicked)
116
118 """ 119 Create the I{QLineEdit }used for selecting API names. This includes a QCompleter to make suggestions based on 120 the keyword database. 121 """ 122 self.api_chooser_lineedit = QLineEdit() 123 self.api_chooser_lineedit.returnPressed.connect(self.populate_browser_window) 124 self.api_chooser_lineedit.textChanged.connect(self.update_completer_model) 125 126 completer = QCompleter() 127 completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive) 128 completer.setModelSorting(QCompleter.CaseSensitivelySortedModel) 129 self.completer_model = QStringListModel([]) 130 completer.setModel(self.completer_model) 131 self.api_chooser_lineedit.setCompleter(completer)
132
133 - def create_search_button(self):
134 """ 135 Create a search button besides the QLineEdit. 136 """ 137 self.search_button = QtGui.QPushButton(self.search_icon, "", self) 138 self.search_button.setToolTip("Search for the chosen API name, structure or whatever WinAPI documentation " \ 139 + "might have for you.") 140 self.search_button.resize(self.search_button.sizeHint()) 141 self.search_button.clicked.connect(self.onSearchButtonClicked)
142
143 - def create_browser_window(self):
144 """ 145 Create the browser window with a I{QTextBrowser}. This display component is chosen over I{QWebView} because 146 WebKit is not included in the standard PySide installation as distributed with IDA Pro. 147 """ 148 self.browser_window = QTextBrowser() 149 self.browser_window.anchorClicked.connect(self.browserAnchorClicked)
150
151 - def update_completer_model(self):
152 """ 153 Update the completer model used to make suggestions. The model is only updated if anything is entered into the 154 search line and the initial character differs from the previous initial character. 155 """ 156 keyword_data = [] 157 api_chooser_text = self.api_chooser_lineedit.text() 158 if len(api_chooser_text) > 0: 159 keyword_initial = api_chooser_text[0].lower() 160 if keyword_initial != self.old_keyword_initial: 161 self.old_keyword_initial = keyword_initial 162 keyword_data = self.winapi.get_keywords_for_initial(keyword_initial) 163 self.completer_model.setStringList(keyword_data)
164
165 - def populate_browser_window(self):
166 """ 167 Populate the browser window based upon the entered term in the search line. 168 """ 169 api_chooser_text = self.api_chooser_lineedit.text() 170 if len(api_chooser_text) > 0: 171 api_document = self.winapi.get_keyword_content(api_chooser_text) 172 self.browser_window.setHtml(api_document) 173 self.update_history_button_state()
174
175 - def onSearchButtonClicked(self):
176 """ 177 Action that is performed when the search button is clicked. This will populate the browser window. 178 """ 179 self.populate_browser_window()
180
181 - def onBackButtonClicked(self):
182 """ 183 Action that is performed when the search button is clicked. This will populate the browser window. 184 """ 185 document_content, anchor = self.winapi.get_previous_document_content() 186 if document_content != "": 187 self.browser_window.setHtml(document_content) 188 self.browser_window.scrollToAnchor(anchor) 189 self.update_history_button_state()
190
191 - def onNextButtonClicked(self):
192 """ 193 Action that is performed when the search button is clicked. This will populate the browser window. 194 """ 195 document_content, anchor = self.winapi.get_next_document_content() 196 if document_content != "": 197 self.browser_window.setHtml(document_content) 198 self.browser_window.scrollToAnchor(anchor) 199 self.update_history_button_state()
200
201 - def browserAnchorClicked(self, url):
202 """ 203 Callback for the case an anchor (or any link) within the browser window is clicked. This will fetch 204 document content and anchor based on the URL of the link and update the browser window. 205 @param url: a URL as triggered by the callback 206 @type url: QUrl 207 """ 208 document_content, anchor = self.winapi.get_linked_document_content(url) 209 if document_content != "": 210 self.browser_window.setHtml(document_content) 211 self.browser_window.scrollToAnchor(anchor) 212 self.update_history_button_state()
213
214 - def navigate(self, api_name):
215 """ 216 A function exposed in order to allow the widget to be navigated to an arbitrary API name. 217 @param api_name: the API name to navigate the widget to. 218 @type api_name: str 219 """ 220 self.api_chooser_lineedit.setText(api_name) 221 self.populate_browser_window()
222
224 """ 225 A function exposed to allow navigating the widget to the currently highlighted identifier from the IDA view. 226 """ 227 if self.winapi.has_offline_msdn_available(): 228 highlighted_identifier = self.ida_proxy.get_highlighted_identifier() 229 self.navigate(highlighted_identifier) 230 self.parent.setTabFocus(self.name)
231
233 """ 234 Update the button state (enabled/disabled) according to availability of history information from the 235 WinApiProvider 236 """ 237 history_button_states = self.winapi.get_history_states() 238 self.back_button.setEnabled(history_button_states[0]) 239 self.next_button.setEnabled(history_button_states[1])
240