Coverage for C:\leo.repo\leo-editor\leo\core\leoGui.py: 71%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

381 statements  

1# -*- coding: utf-8 -*- 

2#@+leo-ver=5-thin 

3#@+node:ekr.20031218072017.3719: * @file leoGui.py 

4#@@first 

5""" 

6A module containing the base gui-related classes. 

7 

8These classes hide the details of which gui is actually being used. 

9Leo's core calls this class to allocate all gui objects. 

10 

11Plugins may define their own gui classes by setting g.app.gui. 

12""" 

13from leo.core import leoGlobals as g 

14from leo.core import leoFrame 

15 # for NullGui and StringTextWrapper. 

16#@+others 

17#@+node:ekr.20031218072017.3720: ** class LeoGui 

18class LeoGui: 

19 """The base class of all gui classes. 

20 

21 Subclasses are expected to override all do-nothing methods of this class. 

22 """ 

23 #@+others 

24 #@+node:ekr.20031218072017.3722: *3* LeoGui.__init__ 

25 def __init__(self, guiName): 

26 """Ctor for the LeoGui class.""" 

27 self.active = None # Used only by qt_gui. 

28 self.consoleOnly = True # True if g.es goes to console. 

29 self.globalFindTabManager = None 

30 self.globalFindTab = None 

31 self.idleTimeClass = None 

32 self.isNullGui = False 

33 self.lastFrame = None 

34 self.leoIcon = None 

35 self.mGuiName = guiName 

36 self.mainLoop = None 

37 self.plainTextWidget = None # For SpellTabHandler class only. 

38 self.root = None 

39 self.script = None 

40 self.splashScreen = None 

41 self.utils = None 

42 # To keep pylint happy. 

43 self.ScriptingControllerClass = NullScriptingControllerClass 

44 # 

45 # Define special keys that may be overridden is subclasses. 

46 self.ignoreChars = [] # Keys that should always be ignored. 

47 self.FKeys = [] # The representation of F-keys. 

48 self.specialChars = [] # A list of characters/keys to be handle specially. 

49 #@+node:ekr.20061109212618.1: *3* LeoGui: Must be defined only in base class 

50 #@+node:ekr.20110605121601.18847: *4* LeoGui.create_key_event (LeoGui) 

51 def create_key_event(self, c, 

52 binding=None, char=None, event=None, w=None, 

53 x=None, x_root=None, 

54 y=None, y_root=None, 

55 ): 

56 # Do not call strokeFromSetting here! 

57 # For example, this would wrongly convert Ctrl-C to Ctrl-c, 

58 # in effect, converting a user binding from Ctrl-Shift-C to Ctrl-C. 

59 return LeoKeyEvent(c, char, event, binding, w, x, y, x_root, y_root) 

60 #@+node:ekr.20031218072017.3740: *4* LeoGui.guiName 

61 def guiName(self): 

62 try: 

63 return self.mGuiName 

64 except Exception: 

65 return "invalid gui name" 

66 #@+node:ekr.20031218072017.2231: *4* LeoGui.setScript 

67 def setScript(self, script=None, scriptFileName=None): 

68 self.script = script 

69 self.scriptFileName = scriptFileName 

70 #@+node:ekr.20110605121601.18845: *4* LeoGui.event_generate (LeoGui) 

71 def event_generate(self, c, char, shortcut, w): 

72 event = self.create_key_event(c, binding=shortcut, char=char, w=w) 

73 c.k.masterKeyHandler(event) 

74 c.outerUpdate() 

75 #@+node:ekr.20061109212618: *3* LeoGu: Must be defined in subclasses 

76 #@+node:ekr.20031218072017.3725: *4* LeoGui.destroySelf 

77 def destroySelf(self): 

78 self.oops() 

79 #@+node:ekr.20031218072017.3730: *4* LeoGui.dialogs 

80 def runAboutLeoDialog(self, c, version, theCopyright, url, email): 

81 """Create and run Leo's About Leo dialog.""" 

82 self.oops() 

83 

84 def runAskLeoIDDialog(self): 

85 """Create and run a dialog to get g.app.LeoID.""" 

86 self.oops() 

87 

88 def runAskOkDialog(self, c, title, message=None, text="Ok"): 

89 """Create and run an askOK dialog .""" 

90 self.oops() 

91 

92 def runAskOkCancelNumberDialog( 

93 self, c, title, message, cancelButtonText=None, okButtonText=None): 

94 """Create and run askOkCancelNumber dialog .""" 

95 self.oops() 

96 

97 def runAskOkCancelStringDialog(self, c, title, message, cancelButtonText=None, 

98 okButtonText=None, default="", wide=False): 

99 """Create and run askOkCancelString dialog .""" 

100 self.oops() 

101 

102 def runAskYesNoDialog(self, c, title, message=None, yes_all=False, no_all=False): 

103 """Create and run an askYesNo dialog.""" 

104 self.oops() 

105 

106 def runAskYesNoCancelDialog(self, c, title, 

107 message=None, yesMessage="Yes", noMessage="No", 

108 yesToAllMessage=None, defaultButton="Yes", cancelMessage=None, 

109 ): 

110 """Create and run an askYesNoCancel dialog .""" 

111 self.oops() 

112 

113 def runPropertiesDialog( 

114 self, title='Properties', data=None, callback=None, buttons=None): 

115 """Dispay a modal TkPropertiesDialog""" 

116 self.oops() 

117 #@+node:ekr.20031218072017.3731: *4* LeoGui.file dialogs 

118 def runOpenFileDialog( 

119 self, c, title, filetypes, defaultextension, multiple=False, startpath=None): 

120 """Create and run an open file dialog .""" 

121 self.oops() 

122 

123 def runSaveFileDialog(self, c, title, filetypes, defaultextension): 

124 """Create and run a save file dialog .""" 

125 self.oops() 

126 #@+node:ekr.20031218072017.3732: *4* LeoGui.panels 

127 def createColorPanel(self, c): 

128 """Create a color panel""" 

129 self.oops() 

130 

131 def createComparePanel(self, c): 

132 """Create Compare panel.""" 

133 self.oops() 

134 

135 def createFindTab(self, c, parentFrame): 

136 """Create a find tab in the indicated frame.""" 

137 self.oops() 

138 

139 def createFontPanel(self, c): 

140 """Create a hidden Font panel.""" 

141 self.oops() 

142 

143 def createLeoFrame(self, c, title): 

144 """Create a new Leo frame.""" 

145 self.oops() 

146 #@+node:ekr.20031218072017.3729: *4* LeoGui.runMainLoop 

147 def runMainLoop(self): 

148 """Run the gui's main loop.""" 

149 self.oops() 

150 #@+node:ekr.20031218072017.3733: *4* LeoGui.utils 

151 #@+at Subclasses are expected to subclass all of the following methods. 

152 # These are all do-nothing methods: callers are expected to check for 

153 # None returns. 

154 # The type of commander passed to methods depends on the type of frame 

155 # or dialog being created. The commander may be a Commands instance or 

156 # one of its subcommanders. 

157 #@+node:ekr.20031218072017.3734: *5* LeoGui.Clipboard 

158 def replaceClipboardWith(self, s): 

159 self.oops() 

160 

161 def getTextFromClipboard(self): 

162 self.oops() 

163 #@+node:ekr.20031218072017.3735: *5* LeoGui.Dialog utils 

164 def attachLeoIcon(self, window): 

165 """Attach the Leo icon to a window.""" 

166 self.oops() 

167 

168 def center_dialog(self, dialog): 

169 """Center a dialog.""" 

170 self.oops() 

171 

172 def create_labeled_frame( 

173 self, parent, caption=None, relief="groove", bd=2, padx=0, pady=0): 

174 """Create a labeled frame.""" 

175 self.oops() 

176 

177 def get_window_info(self, window): 

178 """Return the window information.""" 

179 self.oops() 

180 #@+node:ekr.20031218072017.3737: *5* LeoGui.Focus 

181 def get_focus(self, *args, **kwargs): 

182 """Return the widget that has focus, or the body widget if None.""" 

183 self.oops() 

184 

185 def set_focus(self, commander, widget): 

186 """Set the focus of the widget in the given commander if it needs to be changed.""" 

187 self.oops() 

188 #@+node:ekr.20031218072017.3736: *5* LeoGui.Font 

189 def getFontFromParams(self, family, size, slant, weight, defaultSize=12): 

190 

191 self.oops() 

192 #@+node:ekr.20070212145124: *5* LeoGui.getFullVersion 

193 def getFullVersion(self, c=None): 

194 return 'LeoGui: dummy version' 

195 #@+node:ekr.20070212070820: *5* LeoGui.makeScriptButton 

196 def makeScriptButton(self, c, 

197 args=None, 

198 p=None, 

199 script=None, 

200 buttonText=None, 

201 balloonText='Script Button', 

202 shortcut=None, 

203 bg='LightSteelBlue1', 

204 define_g=True, 

205 define_name='__main__', 

206 silent=False, 

207 ): 

208 self.oops() 

209 #@+node:ekr.20070228154059: *3* LeoGui: May be defined in subclasses 

210 #@+node:ekr.20110613103140.16423: *4* LeoGui.dismiss_spash_screen 

211 def dismiss_splash_screen(self): 

212 pass # May be overridden in subclasses. 

213 #@+node:tbrown.20110618095626.22068: *4* LeoGui.ensure_commander_visible 

214 def ensure_commander_visible(self, c): 

215 """E.g. if commanders are in tabs, make sure c's tab is visible""" 

216 pass 

217 #@+node:ekr.20070219084912: *4* LeoGui.finishCreate 

218 def finishCreate(self): 

219 # This may be overridden in subclasses. 

220 pass 

221 #@+node:ekr.20101028131948.5861: *4* LeoGui.killPopupMenu & postPopupMenu 

222 # These definitions keep pylint happy. 

223 

224 def postPopupMenu(self, *args, **keys): 

225 pass 

226 #@+node:ekr.20031218072017.3741: *4* LeoGui.oops 

227 def oops(self): 

228 # It is not usually an error to call methods of this class. 

229 # However, this message is useful when writing gui plugins. 

230 if 1: 

231 g.pr("LeoGui oops", g.callers(4), "should be overridden in subclass") 

232 #@+node:ekr.20170612065049.1: *4* LeoGui.put_help 

233 def put_help(self, c, s, short_title): 

234 pass 

235 #@+node:ekr.20051206103652: *4* LeoGui.widget_name (LeoGui) 

236 def widget_name(self, w): 

237 # First try the widget's getName method. 

238 if not 'w': 

239 return '<no widget>' 

240 if hasattr(w, 'getName'): 

241 return w.getName() 

242 if hasattr(w, '_name'): 

243 return w._name 

244 return repr(w) 

245 #@-others 

246#@+node:ekr.20070228160107: ** class LeoKeyEvent 

247class LeoKeyEvent: 

248 """A gui-independent wrapper for gui events.""" 

249 #@+others 

250 #@+node:ekr.20110605121601.18846: *3* LeoKeyEvent.__init__ 

251 def __init__(self, c, char, event, binding, w, 

252 x=None, y=None, x_root=None, y_root=None 

253 ): 

254 """Ctor for LeoKeyEvent class.""" 

255 if g.isStroke(binding): 

256 g.trace('***** (LeoKeyEvent) oops: already a stroke', binding, g.callers()) 

257 stroke = binding 

258 else: 

259 stroke = g.KeyStroke(binding) if binding else None 

260 assert g.isStrokeOrNone(stroke), f"(LeoKeyEvent) {stroke!r} {g.callers()}" 

261 if 0: # Doesn't add much. 

262 if 'keys' in g.app.debug: 

263 print(f"LeoKeyEvent: binding: {binding}, stroke: {stroke}, char: {char!r}") 

264 self.c = c 

265 self.char = char or '' 

266 self.event = event # New in Leo 4.11. 

267 self.stroke = stroke 

268 self.w = self.widget = w 

269 # Optional ivars 

270 self.x = x 

271 self.y = y 

272 # Support for fastGotoNode plugin 

273 self.x_root = x_root 

274 self.y_root = y_root 

275 #@+node:ekr.20140907103315.18774: *3* LeoKeyEvent.__repr__ 

276 def __repr__(self): 

277 

278 d = {'c': self.c.shortFileName()} 

279 for ivar in ('char', 'event', 'stroke', 'w'): 

280 d[ivar] = getattr(self, ivar) 

281 return f"LeoKeyEvent:\n{g.objToString(d)}" 

282 #@+node:ekr.20150511181702.1: *3* LeoKeyEvent.get & __getitem__ 

283 def get(self, attr): 

284 """Compatibility with g.bunch: return an attr.""" 

285 return getattr(self, attr, None) 

286 

287 def __getitem__(self, attr): 

288 """Compatibility with g.bunch: return an attr.""" 

289 return getattr(self, attr, None) 

290 #@+node:ekr.20140907103315.18775: *3* LeoKeyEvent.type 

291 def type(self): 

292 return 'LeoKeyEvent' 

293 #@-others 

294#@+node:ekr.20031218072017.2223: ** class NullGui (LeoGui) 

295class NullGui(LeoGui): 

296 """Null gui class.""" 

297 #@+others 

298 #@+node:ekr.20031218072017.2225: *3* NullGui.__init__ 

299 def __init__(self, guiName='nullGui'): 

300 """ctor for the NullGui class.""" 

301 super().__init__(guiName) 

302 self.clipboardContents = '' 

303 self.focusWidget = None 

304 self.script = None 

305 self.lastFrame = None # The outer frame, to set g.app.log in runMainLoop. 

306 self.isNullGui = True 

307 self.idleTimeClass = g.NullObject 

308 #@+node:ekr.20031218072017.3744: *3* NullGui.dialogs 

309 def runAboutLeoDialog(self, c, version, theCopyright, url, email): 

310 return self.simulateDialog("aboutLeoDialog", None) 

311 

312 def runAskLeoIDDialog(self): 

313 return self.simulateDialog("leoIDDialog", None) 

314 

315 def runAskOkDialog(self, c, title, message=None, text="Ok"): 

316 return self.simulateDialog("okDialog", "Ok") 

317 

318 def runAskOkCancelNumberDialog(self, c, title, message, 

319 cancelButtonText=None, 

320 okButtonText=None, 

321 ): 

322 return self.simulateDialog("numberDialog", -1) 

323 

324 def runAskOkCancelStringDialog(self, c, title, message, 

325 cancelButtonText=None, 

326 okButtonText=None, 

327 default="", 

328 wide=False, 

329 ): 

330 return self.simulateDialog("stringDialog", '') 

331 

332 def runCompareDialog(self, c): 

333 return self.simulateDialog("compareDialog", '') 

334 

335 def runOpenFileDialog(self, c, title, filetypes, defaultextension, 

336 multiple=False, 

337 startpath=None, 

338 ): 

339 return self.simulateDialog("openFileDialog", None) 

340 

341 def runSaveFileDialog(self, c, title, filetypes, defaultextension): 

342 return self.simulateDialog("saveFileDialog", None) 

343 

344 def runAskYesNoDialog(self, c, title, 

345 message=None, 

346 yes_all=False, 

347 no_all=False, 

348 ): 

349 return self.simulateDialog("yesNoDialog", "no") 

350 

351 def runAskYesNoCancelDialog(self, c, title, 

352 message=None, 

353 yesMessage="Yes", 

354 noMessage="No", 

355 yesToAllMessage=None, 

356 defaultButton="Yes", 

357 cancelMessage=None, 

358 ): 

359 return self.simulateDialog("yesNoCancelDialog", "cancel") 

360 

361 def simulateDialog(self, key, defaultVal): 

362 return defaultVal 

363 #@+node:ekr.20170613101737.1: *3* NullGui.clipboard & focus 

364 def get_focus(self, *args, **kwargs): 

365 return self.focusWidget 

366 

367 def getTextFromClipboard(self): 

368 return self.clipboardContents 

369 

370 def replaceClipboardWith(self, s): 

371 self.clipboardContents = s 

372 

373 def set_focus(self, commander, widget): 

374 self.focusWidget = widget 

375 #@+node:ekr.20070301171901: *3* NullGui.do nothings 

376 def alert(self, c, message): 

377 pass 

378 

379 def attachLeoIcon(self, window): 

380 pass 

381 

382 def destroySelf(self): 

383 pass 

384 

385 def finishCreate(self): 

386 pass 

387 

388 def getFontFromParams(self, family, size, slant, weight, defaultSize=12): 

389 return g.app.config.defaultFont 

390 

391 def getIconImage(self, name): 

392 return None 

393 

394 def getImageImage(self, name): 

395 return None 

396 

397 def getTreeImage(self, c, path): 

398 return None 

399 

400 def get_window_info(self, window): 

401 return 600, 500, 20, 20 

402 

403 def onActivateEvent(self, *args, **keys): 

404 pass 

405 

406 def onDeactivateEvent(self, *args, **keys): 

407 pass 

408 

409 def set_top_geometry(self, w, h, x, y): 

410 pass 

411 #@+node:ekr.20070228155807: *3* NullGui.isTextWidget & isTextWrapper 

412 def isTextWidget(self, w): 

413 return True # Must be True for unit tests. 

414 

415 def isTextWrapper(self, w): 

416 """Return True if w is a Text widget suitable for text-oriented commands.""" 

417 return w and getattr(w, 'supportsHighLevelInterface', None) 

418 #@+node:ekr.20031218072017.2230: *3* NullGui.oops 

419 def oops(self): 

420 g.trace("NullGui", g.callers(4)) 

421 #@+node:ekr.20070301172456: *3* NullGui.panels 

422 def createComparePanel(self, c): 

423 """Create Compare panel.""" 

424 self.oops() 

425 

426 def createFindTab(self, c, parentFrame): 

427 """Create a find tab in the indicated frame.""" 

428 pass # Now always done during startup. 

429 

430 def createLeoFrame(self, c, title): 

431 """Create a null Leo Frame.""" 

432 gui = self 

433 self.lastFrame = leoFrame.NullFrame(c, title, gui) 

434 return self.lastFrame 

435 #@+node:ekr.20031218072017.2229: *3* NullGui.runMainLoop 

436 def runMainLoop(self): 

437 """Run the null gui's main loop.""" 

438 if self.script: 

439 frame = self.lastFrame 

440 g.app.log = frame.log 

441 self.lastFrame.c.executeScript(script=self.script) 

442 else: 

443 print('**** NullGui.runMainLoop: terminating Leo.') 

444 # Getting here will terminate Leo. 

445 #@-others 

446#@+node:ekr.20080707150137.5: ** class NullScriptingControllerClass 

447class NullScriptingControllerClass: 

448 """A default, do-nothing class to be overridden by mod_scripting or other plugins. 

449 

450 This keeps pylint happy.""" 

451 

452 def __init__(self, c, iconBar=None): 

453 self.c = c 

454 self.iconBar = iconBar 

455 

456 def createAllButtons(self): 

457 pass 

458#@+node:ekr.20171128093401.1: ** class StringCheckBox (leoGui.py) 

459class StringCheckBox: 

460 """Simulate a QCheckBox.""" 

461 

462 def __init__(self, name, label=None): 

463 self.label = label 

464 self.name = name 

465 self.value = True 

466 

467 def checkState(self): 

468 return self.value 

469 

470 isChecked = checkState 

471 

472 def objectName(self): 

473 return self.name 

474 

475 def setCheckState(self, value): 

476 self.value = value 

477 

478 def toggle(self): 

479 self.value = not self.value 

480#@+node:ekr.20210221130549.1: ** class StringFindTabManager (leoGui.py) (new) 

481class StringFindTabManager: 

482 """A string-based FindTabManager class for unit tests.""" 

483 #@+others 

484 #@+node:ekr.20210221130549.2: *3* sftm.ctor 

485 #@@nobeautify 

486 

487 def __init__(self, c): 

488 """Ctor for the FindTabManager class.""" 

489 self.c = c 

490 self.entry_focus = None # Accessed directly from code(!) 

491 # Find/change text boxes... 

492 self.find_findbox = StringLineEdit('find_text') 

493 self.find_replacebox = StringLineEdit('change_text') 

494 # Check boxes... 

495 self.check_box_ignore_case = StringCheckBox('ignore_case') 

496 self.check_box_mark_changes = StringCheckBox('mark_changes') 

497 self.check_box_mark_finds = StringCheckBox('mark_finds') 

498 self.check_box_regexp = StringCheckBox('pattern_match') 

499 self.check_box_search_body = StringCheckBox('search_body') 

500 self.check_box_search_headline = StringCheckBox('search_headline') 

501 self.check_box_whole_word = StringCheckBox('whole_word') 

502 # Radio buttons... 

503 self.radio_button_entire_outline = StringRadioButton('entire_outline') 

504 self.radio_button_node_only = StringRadioButton('node_only') 

505 self.radio_button_suboutline_only = StringRadioButton('suboutline_only') 

506 # Init the default values. 

507 self.init_widgets() 

508 #@+node:ekr.20210221130549.5: *3* sftm.clear_focus & init_focus & set_entry_focus 

509 def clear_focus(self): 

510 pass 

511 

512 def init_focus(self): 

513 pass 

514 

515 def set_entry_focus(self): 

516 pass 

517 #@+node:ekr.20210221130549.4: *3* sftm.get_settings 

518 #@@nobeautify 

519 

520 def get_settings(self): 

521 """ 

522 Return a g.bunch representing all widget values. 

523 

524 Similar to LeoFind.default_settings, but only for find-tab values. 

525 """ 

526 return g.Bunch( 

527 # Find/change strings... 

528 find_text = self.find_findbox.text(), 

529 change_text = self.find_replacebox.text(), 

530 # Find options... 

531 ignore_case = self.check_box_ignore_case.isChecked(), 

532 mark_changes = self.check_box_mark_changes.isChecked(), 

533 mark_finds = self.check_box_mark_finds.isChecked(), 

534 node_only = self.radio_button_node_only.isChecked(), 

535 pattern_match = self.check_box_regexp.isChecked(), 

536 search_body = self.check_box_search_body.isChecked(), 

537 search_headline = self.check_box_search_headline.isChecked(), 

538 suboutline_only = self.radio_button_suboutline_only.isChecked(), 

539 whole_word = self.check_box_whole_word.isChecked(), 

540 ) 

541 #@+node:ekr.20210221130549.7: *3* sftm.init_widgets 

542 def init_widgets(self): 

543 """ 

544 Init widgets and ivars from c.config settings. 

545 Create callbacks that always keep the LeoFind ivars up to date. 

546 """ 

547 c, find = self.c, self.c.findCommands 

548 # Find/change text boxes. 

549 table1 = ( 

550 ('find_findbox', 'find_text', '<find pattern here>'), 

551 ('find_replacebox', 'change_text', ''), 

552 ) 

553 for widget_ivar, setting_name, default in table1: 

554 w = getattr(self, widget_ivar) 

555 s = c.config.getString(setting_name) or default 

556 w.insert(s) 

557 # Check boxes. 

558 table2 = ( 

559 ('ignore_case', 'check_box_ignore_case'), 

560 ('mark_changes', 'check_box_mark_changes'), 

561 ('mark_finds', 'check_box_mark_finds'), 

562 ('pattern_match', 'check_box_regexp'), 

563 ('search_body', 'check_box_search_body'), 

564 ('search_headline', 'check_box_search_headline'), 

565 ('whole_word', 'check_box_whole_word'), 

566 ) 

567 for setting_name, widget_ivar in table2: 

568 w = getattr(self, widget_ivar) 

569 val = c.config.getBool(setting_name, default=False) 

570 setattr(find, setting_name, val) 

571 if val != w.isChecked(): # Support leoInteg. 

572 w.toggle() 

573 # Radio buttons 

574 table3 = ( 

575 ('node_only', 'node_only', 'radio_button_node_only'), 

576 ('entire_outline', None, 'radio_button_entire_outline'), 

577 ('suboutline_only', 'suboutline_only', 'radio_button_suboutline_only'), 

578 ) 

579 for setting_name, ivar, widget_ivar in table3: 

580 w = getattr(self, widget_ivar) 

581 val = c.config.getBool(setting_name, default=False) 

582 if ivar is not None: 

583 assert hasattr(find, setting_name), setting_name 

584 setattr(find, setting_name, val) 

585 if val != w.isChecked(): 

586 w.toggle() 

587 # Ensure one radio button is set. 

588 if not find.node_only and not find.suboutline_only: 

589 w = self.radio_button_entire_outline 

590 if val != w.isChecked(): 

591 w.toggle() 

592 #@+node:ekr.20210312122351.1: *3* sftm.set_body_and_headline_checkbox 

593 def set_body_and_headline_checkbox(self): 

594 """Return the search-body and search-headline checkboxes to their defaults.""" 

595 # #1840: headline-only one-shot 

596 c = self.c 

597 find = c.findCommands 

598 if not find: 

599 return 

600 table = ( 

601 ('search_body', self.check_box_search_body), 

602 ('search_headline', self.check_box_search_headline), 

603 ) 

604 for setting_name, w in table: 

605 val = c.config.getBool(setting_name, default=False) 

606 if val != w.isChecked(): 

607 w.toggle() 

608 #@+node:ekr.20210221130549.8: *3* sftm.set_radio_button 

609 #@@nobeautify 

610 

611 def set_radio_button(self, name): 

612 """Set the value of the radio buttons""" 

613 d = { 

614 'node-only': self.radio_button_node_only, 

615 'entire-outline': self.radio_button_entire_outline, 

616 'suboutline-only': self.radio_button_suboutline_only, 

617 } 

618 w = d.get(name) 

619 if not w.isChecked(): 

620 w.toggle() 

621 #@+node:ekr.20210221130549.3: *3* sftm.text getters/setters 

622 def get_find_text(self): 

623 s = self.find_findbox.text() 

624 if s and s[-1] in ('\r', '\n'): 

625 s = s[:-1] 

626 return s 

627 

628 def get_change_text(self): 

629 s = self.find_replacebox.text() 

630 if s and s[-1] in ('\r', '\n'): 

631 s = s[:-1] 

632 return s 

633 

634 def set_find_text(self, s): 

635 w = self.find_findbox 

636 w.clear() 

637 w.insert(s) 

638 

639 def set_change_text(self, s): 

640 w = self.find_replacebox 

641 w.clear() 

642 w.insert(s) 

643 #@+node:ekr.20210221130549.9: *3* sftm.toggle_checkbox 

644 #@@nobeautify 

645 

646 def toggle_checkbox(self, checkbox_name): 

647 """Toggle the value of the checkbox whose name is given.""" 

648 d = { 

649 'ignore_case': self.check_box_ignore_case, 

650 'mark_changes': self.check_box_mark_changes, 

651 'mark_finds': self.check_box_mark_finds, 

652 'pattern_match': self.check_box_regexp, 

653 'search_body': self.check_box_search_body, 

654 'search_headline': self.check_box_search_headline, 

655 'whole_word': self.check_box_whole_word, 

656 } 

657 w = d.get(checkbox_name) 

658 w.toggle() 

659 #@-others 

660#@+node:ekr.20170613095422.1: ** class StringGui (LeoGui) 

661class StringGui(LeoGui): 

662 """ 

663 A class representing all on-screen objects using subclasses of the 

664 leoFrame.StringTextWrapper class. 

665 """ 

666 #@+others 

667 #@+node:ekr.20170613095422.7: *3* StringGui.oops 

668 def oops(self): 

669 

670 g.trace("StringGui", g.callers(4)) 

671 #@+node:ekr.20170613114120.1: *3* StringGui.runMainLoop 

672 def runMainLoop(self): 

673 self.oops() 

674 #@-others 

675#@+node:ekr.20171128093503.1: ** class StringLineEdit (leoGui) 

676class StringLineEdit: 

677 """Simulate a QLineEdit.""" 

678 

679 def __init__(self, name, disabled=False): 

680 self.disabled = disabled 

681 self.name = name 

682 self.pos = 0 

683 self.s = '' 

684 

685 def clear(self): 

686 self.pos = 0 

687 self.s = '' 

688 

689 def insert(self, s): 

690 if s: 

691 i = self.pos 

692 self.s = self.s[:i] + s + self.s[i:] 

693 self.pos += len(s) 

694 

695 def objectName(self): 

696 return self.name 

697 

698 def text(self): 

699 return self.s 

700#@+node:ekr.20171128093602.1: ** class StringRadioButton (leoGui.py) 

701class StringRadioButton: 

702 """Simulate a QRadioButton.""" 

703 

704 def __init__(self, name, label=None): 

705 self.label = label 

706 self.name = name 

707 self.value = True 

708 

709 def isChecked(self): 

710 return self.value 

711 

712 def objectName(self): 

713 return self.name 

714 

715 def toggle(self): 

716 self.value = not self.value 

717#@+node:ekr.20031218072017.3742: ** class UnitTestGui (NullGui) 

718class UnitTestGui(NullGui): 

719 """A gui class for use by unit tests.""" 

720 # Presently used only by the import/export unit tests. 

721 #@+others 

722 #@+node:ekr.20031218072017.3743: *3* UnitTestGui.__init__ 

723 def __init__(self, theDict=None): 

724 """ctor for the UnitTestGui class.""" 

725 self.oldGui = g.app.gui 

726 super().__init__("UnitTestGui") 

727 self.theDict = {} if theDict is None else theDict 

728 g.app.gui = self 

729 

730 def destroySelf(self): 

731 g.app.gui = self.oldGui 

732 #@+node:ekr.20071128094234.1: *3* UnitTestGui.createSpellTab 

733 def createSpellTab(self, c, spellHandler, tabName): 

734 pass # This method keeps pylint happy. 

735 #@+node:ekr.20111001155050.15484: *3* UnitTestGui.runAtIdle 

736 if 1: # Huh? 

737 

738 def runAtIdle(self, aFunc): 

739 """Run aFunc immediately for a unit test. 

740 

741 This is a kludge, but it is probably the best that can be done. 

742 """ 

743 aFunc() 

744 #@-others 

745#@-others 

746#@@language python 

747#@@tabwidth -4 

748#@@pagewidth 70 

749#@-leo