1''' Plugin for CudaText editor 2Authors: 3 Andrey Kvichansky (kvichans on github.com) 4Version: 5 '2.3.15 2021-04-02' 6ToDo: (see end of file) 7''' 8 9import re, os, sys, json, collections, itertools, webbrowser, tempfile, html, pickle, time, datetime 10from itertools import * 11from pathlib import PurePath as PPath 12from pathlib import Path as Path 13def first_true(iterable, default=False, pred=None):return next(filter(pred, iterable), default) # 10.1.2. Itertools Recipes 14 15import cudatext as app 16import cudatext_cmd as cmds 17import cudax_lib as apx 18from .cd_plug_lib import * 19 20d = dict 21odict = collections.OrderedDict 22#class odict(collections.OrderedDict): #py3.9 conflict 23# def __init__(self, *args, **kwargs): 24# if args:super().__init__(*args) 25# elif kwargs:super().__init__(kwargs.items()) 26# def __repr__(self): 27# return '{%s}' % (', '.join("'%s':%r" % (k,v) for k,v in self.items())) 28 29pass; LOG = (-1== 1) or apx.get_opt('_opts_dlg_log',False) # Do or dont logging. 30pass; from pprint import pformat 31pass; pf=lambda d:pformat(d,width=150) 32pass; pf80=lambda d:pformat(d,width=80) 33pass; pf60=lambda d:pformat(d,width=60) 34pass; ##!! waits correction 35 36_ = get_translation(__file__) # I18N 37 38MIN_API_VER = '1.0.168' 39MIN_API_VER_4WR = '1.0.175' # vis 40MIN_API_VER = '1.0.231' # listview has prop columns 41MIN_API_VER = '1.0.236' # p, panel 42MIN_API_VER = '1.0.237' # STATUSBAR_SET_CELL_HINT 43VERSION = re.split('Version:', __doc__)[1].split("'")[1] 44VERSION_V, \ 45VERSION_D = VERSION.split(' ') 46MAX_HIST = apx.get_opt('ui_max_history_edits', 20) 47CFG_JSON = app.app_path(app.APP_DIR_SETTINGS)+os.sep+'cuda_options_editor.json' 48HTM_RPT_FILE= str(Path(tempfile.gettempdir()) / 'CudaText_option_report.html') 49FONT_LST = ['default'] \ 50 + [font 51 for font in app.app_proc(app.PROC_ENUM_FONTS, '') 52 if not font.startswith('@')] 53pass; #FONT_LST=FONT_LST[:3] 54 55def load_definitions(defn_path_or_json)->list: 56 """ Return 57 [{ opt:'opt name' 58 , def:<def val> 59 , cmt:'full comment' 60 , frm:'bool'|'float'|'int'|'str'| # simple 61 'int2s'|'strs'|'str2s'| # list/dict 62 'font'|'font-e'| # font non-empty/can-empty 63 '#rgb'|'#rgb-e'| # color non-empty/can-empty 64 'hotk'|'file'|'json'| 65 'unk' 66 , lst:[str] for frm==ints 67 , dct:[(num,str)] for frm==int2s 68 , [(str,str)] for frm==str2s 69 , chp:'chapter/chapter' 70 , tgs:['tag',] 71 }] 72 """ 73 pass; #LOG and log('defn_path_or_json={}',(defn_path_or_json)) 74 kinfs = [] 75 lines = defn_path_or_json \ 76 if str==type(defn_path_or_json) else \ 77 defn_path_or_json.open(encoding='utf8').readlines() 78 if lines[0][0]=='[': 79 # Data is ready - SKIP parsing 80 json_bd = defn_path_or_json \ 81 if str==type(defn_path_or_json) else \ 82 defn_path_or_json.open(encoding='utf8').read() 83 kinfs = json.loads(json_bd, object_pairs_hook=odict) 84 for kinf in kinfs: 85 pass; #LOG and log('opt in kinf={}',('opt' in kinf)) 86 if isinstance(kinf['cmt'], list): 87 kinf['cmt'] = '\n'.join(kinf['cmt']) 88 89 upd_cald_vals(kinfs, '+def') 90 for kinf in kinfs: 91 kinf['jdc'] = kinf.get('jdc', kinf.get('dct', [])) 92 kinf['jdf'] = kinf.get('jdf', kinf.get('def', '')) 93 return kinfs 94 95 l = '\n' 96 97 #NOTE: parse_raw 98 reTags = re.compile(r' *\((#\w+,?)+\)') 99 reN2S = re.compile(r'^\s*(\d+): *(.+)' , re.M) 100 reS2S = re.compile(r'^\s*"(\w*)": *(.+)' , re.M) 101# reLike = re.compile(r' *\(like (\w+)\)') ##?? 102 reFldFr = re.compile(r'\s*Folders from: (.+)') 103 def parse_cmnt(cmnt, frm):#, kinfs): 104 tags= set() 105 mt = reTags.search(cmnt) 106 while mt: 107 tags_s = mt.group(0) 108 tags |= set(tags_s.strip(' ()').replace('#', '').split(',')) 109 cmnt = cmnt.replace(tags_s, '') 110 mt = reTags.search(cmnt) 111 dctN= [[int(m.group(1)), m.group(2).rstrip(', ')] for m in reN2S.finditer(cmnt+l)] 112 dctS= [[ m.group(1) , m.group(2).rstrip(', ')] for m in reS2S.finditer(cmnt+l)] 113 lstF= None 114 mt = reFldFr.search(cmnt) 115 if mt: 116 from_short = mt.group(1) 117 from_dir = from_short if os.path.isabs(from_short) else os.path.join(app.app_path(app.APP_DIR_DATA), from_short) 118 pass; #LOG and log('from_dir={}',(from_dir)) 119 if not os.path.isdir(from_dir): 120 log(_('No folder "{}" from\n{}'), from_short, cmnt) 121 else: 122 lstF = [d for d in os.listdir(from_dir) 123 if os.path.isdir(from_dir+os.sep+d) and d.upper()!='README' and d.strip()] 124 lstF = sorted(lstF) 125 pass; #LOG and log('lstF={}',(lstF)) 126 frm,\ 127 lst = ('strs' , lstF) if lstF else \ 128 (frm , [] ) 129 frm,\ 130 dct = ('int2s', dctN) if dctN else \ 131 ('str2s', dctS) if dctS else \ 132 (frm , [] ) 133 return cmnt, frm, dct, lst, list(tags) 134 #def parse_cmnt 135 def jsstr(s): 136 return s[1:-1].replace(r'\"','"').replace(r'\\','\\') 137 138 reChap1 = re.compile(r' *//\[Section: +(.+)\]') 139 reChap2 = re.compile(r' *//\[(.+)\]') 140 reCmnt = re.compile(r' *//(.+)') 141 reKeyDV = re.compile(r' *"(\w+)" *: *(.+)') 142 reInt = re.compile(r' *(-?\d+)') 143 reFloat = re.compile(r' *(-?\d+\.\d+)') 144 reFontNm= re.compile(r'font\w*_name') 145 reHotkey= re.compile(r'_hotkey_') 146 reColor = re.compile(r'_color$') 147 chap = '' 148 pre_cmnt= '' 149 pre_kinf= None 150 cmnt = '' 151 for line in lines: 152 if False:pass 153 elif reChap1.match(line): 154 mt= reChap1.match(line) 155 chap = mt.group(1) 156 cmnt = '' 157 elif reChap2.match(line): 158 mt= reChap2.match(line) 159 chap = mt.group(1) 160 cmnt = '' 161 elif reCmnt.match(line): 162 mt= reCmnt.match(line) 163 cmnt += l+mt.group(1) 164 elif reKeyDV.match(line): 165 mt= reKeyDV.match(line) 166 key = mt.group(1) 167 dval_s = mt.group(2).rstrip(', ') 168 dfrm,dval= \ 169 ('bool', True ) if dval_s=='true' else \ 170 ('bool', False ) if dval_s=='false' else \ 171 ('float',float(dval_s)) if reFloat.match(dval_s) else \ 172 ('int', int( dval_s)) if reInt.match(dval_s) else \ 173 ('font', dval_s[1:-1] ) if reFontNm.search(key) else \ 174 ('hotk', dval_s[1:-1] ) if reHotkey.search(key) else \ 175 ('#rgb', dval_s[1:-1] ) if reColor.search(key) else \ 176 ('str', jsstr(dval_s)) if dval_s[0]=='"' and dval_s[-1]=='"' else \ 177 ('unk', dval_s ) 178 dfrm,dval=('#rgb-e','' ) if dfrm=='#rgb' and dval=='' else \ 179 (dfrm, dval ) 180 pass; #LOG and log('key,dval_s,dfrm,dval={}',(key,dval_s,dfrm,dval)) 181 182 cmnt = cmnt.strip(l) if cmnt else pre_cmnt 183 ref_frm = cmnt[:3]=='...' 184 pre_cmnt= cmnt if cmnt else pre_cmnt 185 pass; #LOG and log('ref_frm,pre_cmnt,cmnt={}',(ref_frm,pre_cmnt,cmnt)) 186 cmnt = cmnt.lstrip('.'+l) 187 188 dfrm = 'font-e' if dfrm=='font' and _('Empty string is allowed') in cmnt else dfrm 189 190 kinf = odict() 191 kinfs += [kinf] 192 kinf['opt'] = key 193 kinf['def'] = dval 194 kinf['cmt'] = cmnt.strip() 195 kinf['frm'] = dfrm 196 if dfrm in ('int','str'): 197 cmnt,frm,\ 198 dct,lst,tags = parse_cmnt(cmnt, dfrm)#, kinfs) 199 kinf['cmt'] = cmnt.strip() 200 if frm!=dfrm: 201 kinf['frm'] = frm 202 if dct: 203 kinf['dct'] = dct 204 if lst: 205 kinf['lst'] = lst 206 if tags: 207 kinf['tgs'] = tags 208 if dfrm=='font': 209 kinf['lst'] = FONT_LST 210 if dfrm=='font-e': 211 kinf['lst'] = [''] + FONT_LST 212 if chap: 213 kinf['chp'] = chap 214 215 if ref_frm and pre_kinf: 216 # Copy frm data from prev oi 217 pass; #LOG and log('Copy frm pre_kinf={}',(pre_kinf)) 218 kinf[ 'frm'] = pre_kinf['frm'] 219 if 'dct' in pre_kinf: 220 kinf['dct'] = pre_kinf['dct'] 221 if 'lst' in pre_kinf: 222 kinf['lst'] = pre_kinf['lst'] 223 224 pre_kinf= kinf.copy() 225 cmnt = '' 226 #for line 227 pass; #open(str(defn_path_or_json)+'.p.json', 'w').write(json.dumps(kinfs,indent=2)) 228 upd_cald_vals(kinfs, '+def') 229 for kinf in kinfs: 230 kinf['jdc'] = kinf.get('jdc', kinf.get('dct', [])) 231 kinf['jdf'] = kinf.get('jdf', kinf.get('def', '')) 232 return kinfs 233 #def load_definitions 234 235def load_vals(opt_dfns:list, lexr_json='', ed_=None, full=False, user_json='user.json')->odict: 236 """ Create reformated copy (as odict) of 237 definitions data opt_dfns (see load_definitions) 238 If ed_ then add 239 'fval' 240 for some options 241 If full==True then append optitions without definition 242 but only with 243 { opt:'opt name' 244 , frm:'int'|'float'|'str' 245 , uval:<value from user.json> 246 , lval:<value from lexer*.json> 247 }} 248 Return 249 {'opt name':{ opt:'opt name', frm: 250 ? , def:, cmt:, dct:, chp:, tgs: 251 ? , uval:<value from user.json> 252 ? , lval:<value from lexer*.json> 253 ? , fval:<value from ed> 254 }} 255 """ 256 user_json = app.app_path(app.APP_DIR_SETTINGS)+os.sep+user_json 257 lexr_def_json = apx.get_def_setting_dir() +os.sep+lexr_json 258 lexr_json = app.app_path(app.APP_DIR_SETTINGS)+os.sep+lexr_json 259 user_vals = apx._json_loads(open(user_json , encoding='utf8').read(), object_pairs_hook=odict) \ 260 if os.path.isfile(user_json) else {} 261 lexr_def_vals = apx._json_loads(open(lexr_def_json, encoding='utf8').read(), object_pairs_hook=odict) \ 262 if os.path.isfile(lexr_def_json) else {} 263 lexr_vals = apx._json_loads(open(lexr_json , encoding='utf8').read(), object_pairs_hook=odict) \ 264 if os.path.isfile(lexr_json) else {} 265 pass; #LOG and log('lexr_vals={}',(lexr_vals)) 266 pass; #LOG and log('lexr_def_vals={}',(lexr_def_vals)) 267 268 # Fill vals for defined opt 269 pass; #LOG and log('no opt={}',([oi for oi in opt_dfns if 'opt' not in oi])) 270 oinf_valed = odict([(oi['opt'], oi) for oi in opt_dfns]) 271 for opt, oinf in oinf_valed.items(): 272 if opt in lexr_def_vals: # Correct def-vals for lexer 273 oinf['dlx'] = True 274 oinf['def'] = lexr_def_vals[opt] 275 oinf['jdf'] = oinf['def'] 276 if opt in user_vals: # Found user-val for defined opt 277 oinf['uval'] = user_vals[opt] 278 if opt in lexr_vals: # Found lexer-val for defined opt 279 oinf['lval'] = lexr_vals[opt] 280 if ed_ and opt in apx.OPT2PROP: # Found file-val for defined opt 281 fval = ed_.get_prop(apx.OPT2PROP[opt]) 282 oinf['fval'] =fval 283 284 if full: 285 # Append item for non-defined opt 286 reFontNm = re.compile(r'font\w*_name') 287 def val2frm(val, opt=''): 288 pass; #LOG and log('opt,val={}',(opt,val)) 289 return ('bool' if isinstance(val, bool) else 290 'int' if isinstance(val, int) else 291 'float' if isinstance(val, float) else 292 'json' if isinstance(val, (list, dict)) else 293 'hotk' if '_hotkey_' in val else 294 'font' if isinstance(val, str) and 295 reFontNm.search(val) else 296 'str') 297 for uop,uval in user_vals.items(): 298 if uop in oinf_valed: continue 299 oinf_valed[uop] = odict( 300 [ ('opt' ,uop) 301 , ('frm' ,val2frm(uval,uop)) 302 , ('uval' ,uval) 303 ]+([('lval' ,lexr_vals[uop])] if uop in lexr_vals else []) 304 ) 305 for lop,lval in lexr_vals.items(): 306 if lop in oinf_valed: continue 307 oinf_valed[lop] = odict( 308 [ ('opt' ,lop) 309 , ('frm' ,val2frm(lval,lop)) 310 , ('lval' ,lval) 311 ]) 312 313 upd_cald_vals(oinf_valed) 314 upd_cald_vals(oinf_valed, '+def') if lexr_def_vals else None # To update oi['jdf'] by oi['def'] 315 316 return oinf_valed 317 #def load_vals 318 319def upd_cald_vals(ois, what=''): 320 # Fill calculated attrs 321 if '+def' in what: 322 for oi in [oi for oi in ois if 'dct' in oi]: 323 dct = oi['dct'] 324 dval= oi['def'] 325 dc = odict(dct) 326 pass; #LOG and log('dct={}',(dct)) 327 oi['jdc'] = [f('({}) {}', vl, cm ) for vl,cm in dct] 328 oi['jdf'] = f('({}) {}', dval, dc[dval]) 329 pass; #LOG and log('oi={}',(oi)) 330 331 332 # Fill calculated attrs 333 if not what or '+clcd' in what: 334 for op, oi in ois.items(): 335 oi['!'] = ('L' if oi.get('dlx') else '') \ 336 + ('+!!' if 'def' not in oi and 'lval' in oi else 337 '+!' if 'def' not in oi and 'uval' in oi else 338 '!!!' if 'fval' in oi 339 and oi['fval'] != oi.get('lval' 340 , oi.get('uval' 341 , oi.get( 'def'))) else 342 '!!' if 'lval' in oi else 343 '!' if 'uval' in oi else 344 '') 345 dct = odict(oi.get('dct', [])) 346 oi['juvl'] = oi.get('uval', '') \ 347 if not dct or 'uval' not in oi else \ 348 f('({}) {}', oi['uval'], dct[oi['uval']]) 349 oi['jlvl'] = oi.get('lval', '') \ 350 if not dct or 'lval' not in oi else \ 351 f('({}) {}', oi['lval'], dct[oi['lval']]) 352 oi['jfvl'] = oi.get('fval', '') \ 353 if not dct or 'fval' not in oi else \ 354 f('({}) {}', oi['fval'], dct[oi['fval']]) 355 #def upd_cald_vals 356 357#class OptDt: 358# """ Options infos to view/change in dlg. 359# Opt getting is direct - by fields. 360# Opt setting only by methods. 361# """ 362# 363# def __init__(self 364# , keys_info=None # Ready data 365# , path_raw_keys_info='' # default.json 366# , path_svd_keys_info='' # To save parsed default.json 367# , bk_sets=False # Create backup of settings before the first change 368# ): 369# self.defn_path = Path(path_raw_keys_info) 370# self.bk_sets = bk_sets # Need to backup 371# self.bk_files = {} # Created backup files 372# 373# self.opts_defn = {} # Meta-info for options: format, comment, dict/list of values, chapter, tags 374# self.ul_opts = {} # Total options info for user+cur_lexer 375# #def __init__ 376# 377# #class OptDt 378 379_SORT_NO = -1 380_SORT_DN = 0 381_SORT_UP = 1 382_SORT_TSGN = {_SORT_NO:'', _SORT_UP:'↑', _SORT_DN:'↓'} 383_SORT_NSGN = {-1:'', 0:'', 1:'²', 2:'³'} 384_SORT_NSGN.update({n:str(1+n) for n in range(3,10)}) 385_sort_pfx = lambda to,num: '' if to==_SORT_NO else _SORT_TSGN[to]+_SORT_NSGN[num]+' ' 386_next_sort = lambda to: ((1 + 1+to) % 3) - 1 387_inve_sort = lambda to: 1 - to 388 389sorts_dflt = lambda cols: [[_SORT_NO, -1] for c in range(cols)] 390sorts_sign = lambda sorts, col: _sort_pfx(sorts[col][0], sorts[col][1]) 391sorts_on = lambda sorts, col: sorts[col][0] != _SORT_NO 392 393def sorts_turn(sorts, col, scam=''): 394 """ Switch one of sorts """ 395 max_num = max(tn[1] for tn in sorts) 396 tn_col = sorts[col] 397 if 0:pass 398 elif 'c'==scam and tn_col[1]==max_num: # Turn col with max number 399 tn_col[0] = _next_sort(tn_col[0]) 400 tn_col[1] = -1 if tn_col[0]==_SORT_NO else tn_col[1] 401 elif 'c'==scam: # Add new or turn other col 402 tn_col[0] = _next_sort(tn_col[0]) if -1==tn_col[1] else _inve_sort(tn_col[0]) 403 tn_col[1] = max_num+1 if -1==tn_col[1] else tn_col[1] 404 else:#not scam: # Only col 405 for cl,tn in enumerate(sorts): 406 tn[0] = _next_sort(tn_col[0]) if cl==col else _SORT_NO 407 tn[1] = 0 if cl==col else -1 408 return sorts 409 #def sorts_turn 410 411def sorts_sort(sorts, tdata): 412 """ Sort tdata (must contain only str) by sorts """ 413 pass; #log('tdata={}',(tdata)) 414 pass; #log('sorts={}',(sorts)) 415 max_num = max(tn[1] for tn in sorts) 416 if -1==max_num: return tdata 417 418 def push(lst, v): 419 lst.append(v) 420 return lst 421 prep_str = lambda s,inv: (chr(0x10FFFF) # To move empty to bottom 422 if not s else 423 s 424 if not inv else 425 ''.join(chr(0x10FFFF - ord(c)) for c in s) # 0x10FFFF from chr() doc 426 ) 427 428 td_keys = [[r] for r in tdata] 429 for srt_n in range(1+max_num): 430 srt_ctn = first_true(((c,tn) for c,tn in enumerate(sorts)), None 431 ,lambda ntn: ntn[1][1]==srt_n) 432 assert srt_ctn is not None 433 srt_c = srt_ctn[0] 434 inv = srt_ctn[1][0]==_SORT_UP 435 td_keys = [push(r, prep_str(r[0][srt_c], inv)) for r in td_keys] 436 td_keys.sort(key=lambda r: r[1:]) 437 tdata = [r[0] for r in td_keys] # Remove appended cols 438 return tdata 439 #def sorts_sort 440 441class OptEdD: 442 SCROLL_W= app.app_proc(app.PROC_GET_GUI_HEIGHT, 'scrollbar') if app.app_api_version()>='1.0.233' else 15 443 COL_SEC = 0 444 COL_NAM = 1 445 COL_OVR = 2 446 COL_DEF = 3 447 COL_USR = 4 448 COL_LXR = 5 449 COL_FIL = 6 450 COL_LEXR= _('Lexer') 451 COL_FILE= _('File "{}"') 452 COL_NMS = (_('Section'), _('Option'), '!', _('Default'), _('User'), COL_LEXR, COL_FILE) 453 COL_MWS = [ 70, 210, 25, 120, 120, 70, 50] # Min col widths 454# COL_MWS = [ 70, 150, 25, 120, 120, 70, 50] # Min col widths 455 COL_N = len(COL_MWS) 456 CMNT_MHT= 60 # Min height of Comment 457 STBR_FLT= 10 458 STBR_ALL= 11 459 STBR_MSG= 12 460 STBR_H = apx.get_opt('ui_statusbar_height',24) 461 462 FILTER_C= _('&Filter') 463 NO_CHAP = _('_no_') 464 CHPS_H = f(_('Choose section to append in "{}".' 465 '\rHold Ctrl to add several sections.' 466 ), FILTER_C).replace('&', '') 467 FLTR_H = _('Suitable options will contain all specified words.' 468 '\r Tips and tricks:' 469 '\r • Add "#" to search the words also in comments.' 470 '\r • Add "@sec" to show options from section with "sec" in name.' 471 '\r Several sections are allowed.' 472 '\r Click item in menu "Section..." with Ctrl to add it.' 473 '\r • To show only overridden options:' 474 '\r - Add "!" to show only User+Lexer+File.' 475 '\r - Add "!!" to show only Lexer+File' 476 '\r - Add "!!!" to show only File.' 477 '\r • Use "<" or ">" for word boundary.' 478 '\r Example: ' 479 '\r size> <tab' 480 '\r selects "tab_size" but not "ui_tab_size" or "tab_size_x".' 481 '\r • Alt+L - Clear filter') 482 LOCV_C = _('Go to "{}" in user/lexer config file') 483 LOCD_C = _('Go to "{}" in default config file') 484 OPME_H = _('Edit JSON value') 485 TOOP_H = f(_('Close dialog and open user/lexer settings file' 486 '\rto edit the current option.' 487 '\rSee also menu command' 488 '\r {}'), f(LOCD_C, '<option>')) 489 LIFL_C = _('Instant filtering') 490 FULL_C = _('Show &all keys in user/lexer configs') 491 492 @staticmethod 493 def prep_sorts(sorts): 494 M = OptEdD 495 if len(sorts)==len(M.COL_NMS): 496 return sorts 497 return sorts_dflt(len(M.COL_NMS)) 498 499 def __init__(self 500 , path_keys_info ='' # default.json or parsed data (file or list_of_dicts) 501 , subset ='' # To get/set from/to cuda_options_editor.json 502 , how ={} # Details to work 503 ): 504 M,m = self.__class__,self 505 506 m.ed = ed 507 m.how = how 508 509 m.defn_path = Path(path_keys_info) if str==type(path_keys_info) else json.dumps(path_keys_info) 510 511 m.subset = subset 512 m.stores = get_hist('dlg' 513 , json.loads(open(CFG_JSON).read(), object_pairs_hook=odict) 514 if os.path.exists(CFG_JSON) else odict()) 515 pass; #LOG and log('ok',()) 516# m.bk_sets = m.stores.get(m.subset+'bk_sets' , False) 517 m.lexr_l = app.lexer_proc(app.LEXER_GET_LEXERS, False) 518 m.lexr_w_l = [f('{} {}' 519 ,'!!' if os.path.isfile(app.app_path(app.APP_DIR_SETTINGS)+os.sep+'lexer '+lxr+'.json') else ' ' 520 , lxr) 521 for lxr in m.lexr_l] 522 523 m.cur_op = m.stores.get(m.subset+'cur_op' , '') # Name of current option 524 m.col_ws = m.stores.get(m.subset+'col_ws' , M.COL_MWS[:]) 525 m.col_ws = m.col_ws if M.COL_N==len(m.col_ws) else M.COL_MWS[:] 526 m.h_cmnt = m.stores.get(m.subset+'cmnt_heght', M.CMNT_MHT) 527 m.sorts = m.stores.get(m.subset+'sorts' , [] ) # Def sorts is no sorts 528 m.live_fltr = m.stores.get(m.subset+'live_fltr' , False) # To filter after each change and no History 529 m.cond_hl = [s for s in m.stores.get(m.subset+'h.cond', []) if s] if not m.live_fltr else [] 530 m.cond_s = '' if M.restart_cond is None else M.restart_cond # String filter 531 m.ops_only = [] # Subset to show (future) 532 533 m.sorts = M.prep_sorts(m.sorts) 534 535 m.lexr = m.ed.get_prop(app.PROP_LEXER_CARET) 536 m.all_ops = m.stores.get(m.subset+'all_ops' , False) # Show also options without definition 537 538 m.opts_defn = {} # Meta-info for options: format, comment, dict of values, chapter, tags 539 m.opts_full = {} # Show all options 540 m.chp_tree = {} # {'Ui':{ops:[], 'kids':{...}, 'path':'Ui/Tabs'} 541 m.pth2chp = {} # path-index for m.chp_tree 542 543 # Cache 544 m.SKWULFs = [] # Last filtered+sorted 545 m.cols = [] # Last info about listview columns 546 m.itms = [] # Last info about listview cells 547 548# m.bk_files = {} 549# m.do_file('backup-user') if m.bk_sets else 0 550 551 m.do_file('load-data') 552 553 m.for_ulf = 'u' # 'u' for User, 'l' for Lexer, 'f' for File 554 m.cur_op = m.cur_op if m.cur_op in m.opts_full else '' # First at start 555 m.cur_in = 0 if m.cur_op else -1 556 557 m.stbr = None # Handle for statusbar_proc 558 m.locate_on_exit = None 559 560 m.chng_rpt = [] # Report of all changes by user 561 m.apply_one = m.stores.get(m.subset+'apply_one', False) # Do one call OpsReloadAndApply on exit 562 m.apply_need= False # Need to call OpsReloadAndApply 563 m.auto4file = m.stores.get(m.subset+'auto4file', True) # Auto reset file value to over value def/user/lex 564 #def __init__ 565 566 def stbr_act(self, tag=None, val='', opts={}): 567 M,m = self.__class__,self 568 if not m.stbr: return 569 app.statusbar_proc(m.stbr, app.STATUSBAR_SET_CELL_TEXT, tag=tag, value=str(val)) 570 #def stbr_act 571 572 def do_file(self, what, data='', opts={}): 573 M,m = self.__class__,self 574 if False:pass 575 elif what=='load-data': 576 pass; #LOG and log('',) 577 m.opts_defn = load_definitions(m.defn_path) 578 pass; #LOG and log('m.opts_defn={}',pf([o for o in m.opts_defn])) 579 pass; #LOG and log('m.opts_defn={}',pf([o for o in m.opts_defn if '2s' in o['frm']])) 580 m.opts_full = load_vals(m.opts_defn 581 ,lexr_json='lexer '+m.lexr+'.json' 582 ,user_json=m.how.get('stor_json', 'user.json') 583 , ed_=m.ed, full=m.all_ops) 584 m.cur_op = m.cur_op if m.cur_op in m.opts_full else '' 585 pass; #LOG and log('m.opts_full={}',pf(m.opts_full)) 586 m.do_file('build-chp-tree') 587 588 elif what=='build-chp-tree': 589 # Build chapter tree 590 m.chp_tree = odict(ops=list(m.opts_full.keys()) 591 ,kids=odict() 592 ,path='') # {chp:{ops:[], kids:{...}, path:'c1/c2'} 593 m.pth2chp = {} # {path:chp} 594 for op,oi in m.opts_full.items(): 595 chp_s = oi.get('chp', M.NO_CHAP) 596 chp_s = chp_s if chp_s else M.NO_CHAP 597 chp_node= m.chp_tree # Start root to move 598 kids = chp_node['kids'] 599 path ='' 600 for chp in chp_s.split('/'): 601 # Move along branch and create nodes if need 602 chp_node = kids.setdefault(chp, odict()) 603 path += ('/'+chp) if path else chp 604 chp_node['path']= path 605 m.pth2chp[path] = chp_node 606 ops_l = chp_node.setdefault('ops', []) 607 ops_l += [op] 608 if not ('/'+chp_s).endswith('/'+chp): # not last 609 kids = chp_node.setdefault('kids', odict()) 610 pass; #LOG and log('m.chp_tree=¶{}',pf60(m.chp_tree)) 611 pass; #LOG and log('m.pth2chp=¶{}',pf60(m.pth2chp)) 612 613 elif what == 'locate_to': 614 to_open = data['path'] 615 find_s = data['find'] 616 app.file_open(to_open) ##!! 617 pass; #log('to_open={}',(to_open)) 618 pass; #log('ed.get_filename()={}',(ed.get_filename())) 619 m.ag.opts['on_exit_focus_to_ed'] = ed 620 # Locate 621 user_opt= app.app_proc(app.PROC_GET_FINDER_PROP, '') \ 622 if app.app_api_version()>='1.0.248' else \ 623 app.app_proc(app.PROC_GET_FIND_OPTIONS, '') # Deprecated 624 pass; #log('ed_to_fcs.get_filename()={}',(ed_to_fcs.get_filename())) 625 pass; #log('ed.get_filename()={}',(ed.get_filename())) 626 pass; #LOG and log('find_s={!r}',(find_s)) 627 ed.cmd(cmds.cmd_FinderAction, chr(1).join(['findnext', find_s, '', 'fa'])) # f - From-caret, a - Wrap 628 if app.app_api_version()>='1.0.248': 629 app.app_proc(app.PROC_SET_FINDER_PROP, user_opt) 630 else: 631 app.app_proc(app.PROC_SET_FIND_OPTIONS, user_opt) # Deprecated 632 633 elif what in ('locate-def', 'locate-opt', 'goto-def', 'goto-opt', ): 634 if not m.cur_op: 635 m.stbr_act(M.STBR_MSG, _('Choose option to find in config file')) 636 return False 637 oi = m.opts_full[m.cur_op] 638 pass; #LOG and log('m.cur_op,oi={}',(m.cur_op,oi)) 639 to_open = '' 640 if what in ('locate-opt', 'goto-opt'): 641 if 'uval' not in oi and m.for_ulf=='u': 642 m.stbr_act(M.STBR_MSG, f(_('No user value for option "{}"'), m.cur_op)) 643 return False 644 if 'lval' not in oi and m.for_ulf=='l': 645 m.stbr_act(M.STBR_MSG, f(_('No lexer "{}" value for option "{}"'), m.lexr, m.cur_op)) 646 return False 647 to_open = 'lexer '+m.lexr+'.json' if m.for_ulf=='l' else 'user.json' 648 to_open = app.app_path(app.APP_DIR_SETTINGS)+os.sep+to_open 649 else: 650 if 'def' not in oi: 651 m.stbr_act(M.STBR_MSG, f(_('No default for option "{}"'), m.cur_op)) 652 return False 653 to_open = str(m.defn_path) 654 if not os.path.exists(to_open): 655 log('No file={}',(to_open)) 656 return False 657 658 find_s = f('"{}"', m.cur_op) 659 if what in ('goto-def', 'goto-opt'): 660 m.locate_on_exit = d(path=to_open, find=find_s) 661 return True # 662 m.do_file('locate_to', d(path=to_open, find=find_s)) 663 return False 664 665 #elif what=='set-dfns': 666 # m.defn_path = data 667 # m.do_file('load-data') 668 # return d(ctrls=odict(m.get_cnts('lvls'))) 669 670 elif what=='set-lexr': 671 m.opts_full = load_vals(m.opts_defn 672 ,lexr_json='lexer '+m.lexr+'.json' 673 ,user_json=m.how.get('stor_json', 'user.json') 674 ,ed_=m.ed, full=m.all_ops) 675 return d(ctrls=odict(m.get_cnts('lvls'))) 676 677 elif what=='out-rprt': 678 if do_report(HTM_RPT_FILE, 'lexer '+m.lexr+'.json', m.ed): 679 webbrowser.open_new_tab('file://' +HTM_RPT_FILE) 680 app.msg_status(_('Opened browser with file ')+HTM_RPT_FILE) 681 682 return [] 683 #def do_file 684 685 def _prep_opt(self, opts='', ind=-1, nm=None): 686 """ Prepare vars to show info about current option by 687 m.cur_op 688 m.lexr 689 Return 690 {} vi-attrs 691 {} en-attrs 692 {} val-attrs 693 {} items-attrs 694 """ 695 M,m = self.__class__,self 696 if opts=='key2ind': 697 opt_nm = nm if nm else m.cur_op 698 m.cur_in= index_1([m.SKWULFs[row][1] for row in range(len(m.SKWULFs))], opt_nm, -1) 699 return m.cur_in 700 701 if opts=='ind2key': 702 opt_in = ind if -1!=ind else m.ag.cval('lvls') 703 m.cur_op= m.SKWULFs[opt_in][1] if -1<opt_in<len(m.SKWULFs) else '' 704 return m.cur_op 705 706 if opts=='fid4ed': 707 if not m.cur_op: return 'lvls' 708 frm = m.opts_full[m.cur_op]['frm'] 709 fid = 'eded' if frm in ('str', 'int', 'float') else \ 710 'edcb' if frm in ('int2s', 'str2s', 'strs', 'font', 'font-e') else \ 711 'edrf' if frm in ('bool',) else \ 712 'brow' if frm in ('hotk', 'file', '#rgb', '#rgb-e') else \ 713 'opjs' if frm in ('json') else \ 714 'lvls' 715 pass; #LOG and log('m.cur_op,frm,fid={}',(m.cur_op,frm,fid)) 716 return fid 717 718 pass; #LOG and log('m.cur_op, m.lexr={}',(m.cur_op, m.lexr)) 719 vis,ens,vas,its,bcl = {},{},{},{},{} 720 vis['edcl'] = vis['dfcl'] = False 721 bcl['edcl'] = bcl['dfcl'] = 0x20000000 722# bcl['eded'] = bcl['dfvl'] = 0x20000000 723 724 ens['eded'] = ens['setd'] = False # All un=F 725 vis['eded'] = vis['edcb']=vis['edrf']=vis['edrt']=vis['brow']=vis['toop']=vis['opjs'] = False # All vi=F 726 vas['eded'] = vas['dfvl']=vas['cmnt']= '' # All ed empty 727 vas['edcb'] = -1 728 vas['edrf'] = vas['edrt'] = False 729 its['edcb'] = [] 730 731 ens['dfvl'] = True 732 ens['tofi'] = m.cur_op in apx.OPT2PROP 733 if m.for_ulf=='l' and m.lexr not in m.lexr_l: 734 # Not selected lexer 735 vis['eded'] = True 736 ens['dfvl'] = False 737 return vis,ens,vas,its,bcl 738 739 if m.for_ulf=='f' and m.cur_op not in apx.OPT2PROP: 740 # No the option for File 741 vis['eded'] = True 742 ens['dfvl'] = False 743 return vis,ens,vas,its,bcl 744 745 if not m.cur_op: 746 # No current option 747 vis['eded'] = True 748 else: 749 # Current option 750 oi = m.opts_full[m.cur_op] 751 pass; #LOG and log('oi={}',(oi)) 752 vas['dfvl'] = str(oi.get('jdf' , '')).replace('True', 'true').replace('False', 'false') 753 vas['uval'] = oi.get('uval', '') 754 vas['lval'] = oi.get('lval', '') 755 vas['fval'] = oi.get('fval', '') 756 vas['cmnt'] = oi.get('cmt' , '') 757 frm = oi['frm'] 758 ulfvl_va = vas['fval'] \ 759 if m.for_ulf=='f' else \ 760 vas['lval'] \ 761 if m.for_ulf=='l' else \ 762 vas['uval'] # Cur val with cur state of "For lexer" 763 ens['eded'] = frm not in ('json', 'hotk', 'file')#, '#rgb', '#rgb-e') 764 ens['setd'] = frm not in ('json',) and ulfvl_va is not None 765 if False:pass 766 elif frm in ('json'): 767# vis['toop'] = True 768 vis['opjs'] = True 769 vis['eded'] = True 770 vas['eded'] = str(ulfvl_va) 771 elif frm in ('str', 'int', 'float'): 772 vis['eded'] = True 773 vas['eded'] = str(ulfvl_va) 774 elif frm in ('hotk', 'file', '#rgb', '#rgb-e'): 775 vis['eded'] = True 776 vis['brow'] = True 777 vas['eded'] = str(ulfvl_va) 778 vis['edcl'] = frm in ('#rgb', '#rgb-e') 779 vis['dfcl'] = frm in ('#rgb', '#rgb-e') 780 bcl['edcl'] = apx.html_color_to_int(ulfvl_va ) if frm in ('#rgb', '#rgb-e') and ulfvl_va else 0x20000000 781 bcl['dfcl'] = apx.html_color_to_int(vas['dfvl'] ) if frm in ('#rgb', '#rgb-e') and vas['dfvl'] else 0x20000000 782 elif frm in ('bool',): 783 vis['edrf'] = True 784 vis['edrt'] = True 785 vas['edrf'] = ulfvl_va is False 786 vas['edrt'] = ulfvl_va is True 787 elif frm in ('int2s', 'str2s'): 788 vis['edcb'] = True 789 ens['edcb'] = True 790 its['edcb'] = oi['jdc'] 791 vas['edcb'] = index_1([k for (k,v) in oi['dct']], ulfvl_va, -1) 792 pass; #LOG and log('ulfvl_va, vas[edcb]={}',(ulfvl_va,vas['edcb'])) 793 elif frm in ('strs','font','font-e'): 794 vis['edcb'] = True 795 ens['edcb'] = True 796 its['edcb'] = oi['lst'] 797 vas['edcb'] = index_1(oi['lst'], ulfvl_va, -1) 798 799 pass; #LOG and log('ulfvl_va={}',(ulfvl_va)) 800 pass; #LOG and log('vis={}',(vis)) 801 pass; #LOG and log('ens={}',(ens)) 802 pass; #LOG and log('vas={}',(vas)) 803 pass; #LOG and log('its={}',(its)) 804 return vis,ens,vas,its,bcl 805 #def _prep_opt 806 807 def show(self 808 , title # For cap of dlg 809 ): 810 M,m = self.__class__,self 811 812 def when_exit(ag): 813 pass; #LOG and log('',()) 814 pass; #pr_ = dlg_proc_wpr(ag.id_dlg, app.DLG_CTL_PROP_GET, name='edch') 815 pass; #log('exit,pr_={}',('edch', {k:v for k,v in pr_.items() if k in ('x','y')})) 816 pass; #log('cols={}',(ag.cattr('lvls', 'cols'))) 817 m.col_ws= [ci['wd'] for ci in ag.cattr('lvls', 'cols')] 818 m.stores[m.subset+'cmnt_heght'] = m.ag.cattr('cmnt', 'h') 819 820 if m.apply_one and m.apply_need: 821 ed.cmd(cmds.cmd_OpsReloadAndApply) 822 823 if m.locate_on_exit: 824 m.do_file('locate_to', m.locate_on_exit) 825 #def when_exit 826 827 repro_py = apx.get_opt('dlg_cuda_options.repro_py') # 'repro_dlg_opted.py' 828 829 m.dlg_min_w = 10 + sum(M.COL_MWS) + M.COL_N + M.SCROLL_W 830 m.dlg_w = 10 + sum(m.col_ws) + M.COL_N + M.SCROLL_W 831 m.dlg_h = 380 + m.h_cmnt +10 + M.STBR_H 832# m.dlg_h = 270 + m.h_cmnt +10 + M.STBR_H 833 pass; #log('m.dlg_w,m.dlg_h={}',(m.dlg_w,m.dlg_h)) 834 m.ag = DlgAgent( 835 form =dict(cap = title + f(' ({})', VERSION_V) 836 ,resize = True 837 ,w = m.dlg_w ,w_min=m.dlg_min_w 838 ,h = m.dlg_h 839 ,on_resize=m.do_resize 840 ) 841 , ctrls=m.get_cnts() 842 , vals =m.get_vals() 843 , fid ='cond' 844 ,options = ({ 845 'gen_repro_to_file':repro_py, #NOTE: repro 846 } if repro_py else {}) 847 ) 848 # Select on pre-show. Reason: linux skip selection event after show 849 m.ag._update_on_call(m.do_sele('lvls', m.ag)) 850 851 m.stbr = app.dlg_proc(m.ag.id_dlg, app.DLG_CTL_HANDLE, name='stbr') 852 app.statusbar_proc(m.stbr, app.STATUSBAR_ADD_CELL , tag=M.STBR_ALL) 853 app.statusbar_proc(m.stbr, app.STATUSBAR_SET_CELL_SIZE , tag=M.STBR_ALL, value=40) 854 app.statusbar_proc(m.stbr, app.STATUSBAR_SET_CELL_ALIGN , tag=M.STBR_ALL, value='R') 855 app.statusbar_proc(m.stbr, app.STATUSBAR_SET_CELL_HINT , tag=M.STBR_ALL, value=_('Number of all options')) 856 app.statusbar_proc(m.stbr, app.STATUSBAR_ADD_CELL , tag=M.STBR_FLT) 857 app.statusbar_proc(m.stbr, app.STATUSBAR_SET_CELL_SIZE , tag=M.STBR_FLT, value=40) 858 app.statusbar_proc(m.stbr, app.STATUSBAR_SET_CELL_ALIGN , tag=M.STBR_FLT, value='R') 859 app.statusbar_proc(m.stbr, app.STATUSBAR_SET_CELL_HINT , tag=M.STBR_FLT, value=_('Number of shown options')) 860 app.statusbar_proc(m.stbr, app.STATUSBAR_ADD_CELL , tag=M.STBR_MSG) 861 app.statusbar_proc(m.stbr, app.STATUSBAR_SET_CELL_AUTOSTRETCH , tag=M.STBR_MSG, value=True) 862 m.stbr_act(M.STBR_ALL, len(m.opts_full)) 863 m.stbr_act(M.STBR_FLT, len(m.opts_full)) 864 865 stor_json = app.app_path(app.APP_DIR_SETTINGS)+os.sep+m.how.get('stor_json', 'user.json') 866 start_mtime = os.path.getmtime(stor_json) if os.path.exists(stor_json) else 0 867 868 m.ag.show(when_exit) 869 m.ag = None 870 871 # Save for next using 872 m.stores[m.subset+'cur_op'] = m.cur_op 873 m.stores[m.subset+'col_ws'] = m.col_ws 874 m.stores[m.subset+'sorts'] = m.sorts 875 if not m.live_fltr: 876 m.stores[m.subset+'h.cond'] = m.cond_hl 877 m.stores[m.subset+'all_ops'] = m.all_ops 878 set_hist('dlg', m.stores) 879 880 return start_mtime != (os.path.getmtime(stor_json) if os.path.exists(stor_json) else 0) 881 #def show 882 883 def get_cnts(self, what=''): 884 M,m = self.__class__,self 885 886 reNotWdChar = re.compile(r'\W') 887 def test_fltr(fltr_s, op, oi): 888 if not fltr_s: return True 889 pass; #LOG and log('fltr_s, op, oi[!]={}',(fltr_s, op, oi['!'])) 890 if '!!!' in fltr_s and '!!!' not in oi['!']: return False 891 if '!!' in fltr_s and '!!' not in oi['!']: return False 892 pass; #LOG and log('skip !!',()) 893 if '!' in fltr_s and '!' not in oi['!']: return False 894 pass; #LOG and log('skip !',()) 895 text = op \ 896 + (' '+oi.get('cmt', '') if '#' in fltr_s else '') 897 text = text.upper() 898 fltr_s = fltr_s.replace('!', '').replace('#', '').upper() 899 if '<' in fltr_s or '>' in fltr_s: 900 text = '·' + reNotWdChar.sub('·', text) + '·' 901 fltr_s = ' ' + fltr_s + ' ' 902 fltr_s = fltr_s.replace(' <', ' ·').replace('> ', '· ') 903 pass; #LOG and log('fltr_s, text={}',(fltr_s, text)) 904 return all(map(lambda c:c in text, fltr_s.split())) 905 #def test_fltr 906 907 def get_tbl_cols(sorts, col_ws): 908 cnms = list(M.COL_NMS) 909 cnms[M.COL_FIL] = f(cnms[M.COL_FIL], m.ed.get_prop(app.PROP_TAB_TITLE)) 910 cols = [d(nm=sorts_sign(sorts, c) + cnms[c] 911 ,wd=col_ws[c] 912 ,mi=M.COL_MWS[c] 913 ) for c in range(M.COL_N)] 914 cols[M.COL_OVR]['al'] = 'C' 915 if m.how.get('hide_fil', False): 916 pos_fil = M.COL_NMS.index(M.COL_FILE) 917 cols[pos_fil]['vi'] = False 918 if m.how.get('hide_lex_fil', False): 919 pos_lex = M.COL_NMS.index(M.COL_LEXR) 920 pos_fil = M.COL_NMS.index(M.COL_FILE) 921 cols[pos_lex]['vi'] = False 922 cols[pos_fil]['vi'] = False 923 return cols 924 #def get_tbl_cols 925 926 def get_tbl_data(opts_full, cond_s, ops_only, sorts, col_ws): 927 # Filter table data 928 pass; #LOG and log('cond_s={}',(cond_s)) 929 pass; #log('opts_full/tab_s={}',({o:oi for o,oi in opts_full.items() if o.startswith('tab_s')})) 930 chp_cond = '' 931 chp_no_c = False 932 if '@' in cond_s: 933 # Prepare to match chapters 934 chp_cond = ' '.join([mt.group(1) for mt in re.finditer(r'@([\w/]+)' , cond_s)]).upper() # @s+ not empty chp 935 chp_cond = chp_cond.replace(M.NO_CHAP.upper(), '').strip() 936 chp_no_c = '@'+M.NO_CHAP in cond_s 937 cond_s = re.sub( r'@([\w/]*)', '', cond_s) # @s* clear @ and cph 938 pass; #log('chp_cond, chp_no_c, cond_s={}',(chp_cond, chp_no_c, cond_s)) 939 SKWULFs = [ (oi.get('chp','') 940 ,op 941 ,oi['!'] 942 ,str(oi.get('jdf' ,'')).replace('True', 'true').replace('False', 'false') 943 ,str(oi.get('juvl','')).replace('True', 'true').replace('False', 'false') 944 ,str(oi.get('jlvl','')).replace('True', 'true').replace('False', 'false') 945 ,str(oi.get('jfvl','')).replace('True', 'true').replace('False', 'false') 946 ,oi['frm'] 947 ) 948 for op,oi in opts_full.items() 949# if (not chp_cond or chp_cond in oi.get('chp', '').upper()) 950 if (not chp_cond or any((chp_cond in oi.get('chp', '').upper()) for chp_cond in chp_cond.split())) 951 and (not chp_no_c or not oi.get('chp', '')) 952 and (not cond_s or test_fltr(cond_s, op, oi)) 953 and (not ops_only or op in ops_only) 954 ] 955 # Sort table data 956 SKWULFs = sorts_sort(sorts, SKWULFs) 957 # Fill table 958 pass; #LOG and log('M.COL_NMS,col_ws,M.COL_MWS={}',(len(M.COL_NMS),len(col_ws),len(M.COL_MWS))) 959 cols = get_tbl_cols(sorts, col_ws) 960 961 itms = (list(zip([_('Section'),_('Option'), '', _('Default'), _('User'), _('Lexer'), _('File')], map(str, col_ws))) 962 #, [ (str(n)+':'+sc,k ,w ,dv ,uv ,lv ,fv) # for debug 963 #, [ (sc+' '+fm ,k ,w ,dv ,uv ,lv ,fv) # for debug 964 , [ (sc ,k ,w ,dv ,uv ,lv ,fv) # for user 965 for n,( sc ,k ,w ,dv ,uv ,lv ,fv, fm) in enumerate(SKWULFs) ] 966 ) 967 return SKWULFs, cols, itms 968 #def get_tbl_data 969 970 if not what or '+lvls' in what: 971 m.SKWULFs,\ 972 m.cols ,\ 973 m.itms = get_tbl_data(m.opts_full, m.cond_s, m.ops_only, m.sorts, m.col_ws) 974 if 'stbr' in dir(m): 975 m.stbr_act(M.STBR_FLT, len(m.SKWULFs)) 976 977 if '+cols' in what: 978 pass; #LOG and log('m.col_ws={}',(m.col_ws)) 979 m.cols = get_tbl_cols(m.sorts, m.col_ws) 980 pass; #LOG and log('m.cols={}',(m.cols)) 981 982 # Prepare [Def]Val data by m.cur_op 983 vis,ens,vas,its,bcl = m._prep_opt() 984 985 ed_s_c = _('>Fil&e:') if m.for_ulf=='f' else \ 986 _('>L&exer:') if m.for_ulf=='l' else \ 987 _('>Us&er:') 988 cnts = [] 989 if '+cond' in what: 990 cnts += [0 991 ,('cond',d(items=m.cond_hl)) 992 ][1:] 993 994 if '+cols' in what or '=cols' in what: 995 cnts += [0 996 ,('lvls',d(cols=m.cols)) 997 ][1:] 998 if '+lvls' in what or '=lvls' in what: 999 cnts += [0 1000 ,('lvls',d(cols=m.cols, items=m.itms)) 1001 ][1:] 1002 1003 tofi_en = not m.how.get('only_for_ul', not ens['tofi']) # Forbid to switch fo File ops 1004 if '+cur' in what: 1005 cnts += [0 1006 ,('ed_s',d(cap=ed_s_c ,hint=m.cur_op )) 1007# ,('eded',d(vis=vis['eded'] ,sto=ens['eded'] ,color=bcl['eded'] )) 1008# ,('eded',d(vis=vis['eded'],ex0=not ens['eded'],sto=ens['eded'] ,color=bcl['eded'] )) 1009# ,('eded',d(vis=vis['eded'],en=ens['eded'] ,color=bcl['eded'] )) 1010 ,('eded',d(vis=vis['eded'],en=ens['eded'] )) 1011 ,('edcl',d(vis=vis['edcl'] ,color=bcl['edcl'] )) 1012 ,('edcb',d(vis=vis['edcb'] ,items=its['edcb'] )) 1013 ,('edrf',d(vis=vis['edrf'] )) 1014 ,('edrt',d(vis=vis['edrt'] )) 1015 ,('brow',d(vis=vis['brow'] )) 1016 ,('toop',d(vis=vis['toop'] )) 1017 ,('opjs',d(vis=vis['opjs'] )) 1018 ,('dfv_',d( hint=m.cur_op )) 1019 ,('dfvl',d( )) 1020# ,('dfvl',d( en=ens['dfvl'] ,color=bcl['dfvl'] )) 1021 ,('dfcl',d(vis=vis['dfcl'] ,color=bcl['dfcl'] )) 1022 ,('setd',d( en=ens['setd'] )) 1023 ,('tofi',d( en=tofi_en )) 1024 ][1:] 1025 1026 if what and cnts: 1027 # Part info 1028 return cnts 1029 1030 # Full dlg controls info #NOTE: cnts 1031 edit_h = get_gui_height('edit') 1032 cmnt_t = m.dlg_h-m.h_cmnt-5-M.STBR_H 1033 tofi_c = m.ed.get_prop(app.PROP_TAB_TITLE) 1034 co_tp = 'ed' if m.live_fltr else 'cb' 1035 cnts = [0 # 1036 # Hidden buttons 1037 ,('flt-',d(tp='bt' ,cap='&l' ,sto=False ,t=-99,l=0,w=44)) # &l 1038 ,('fltr',d(tp='bt' ,cap='' ,sto=False ,def_bt='1' ,t=-99,l=0,w=44)) # Enter 1039 ,('srt0',d(tp='bt' ,cap='&1' ,sto=False ,t=-99,l=0,w=44)) # &1 1040 ,('srt1',d(tp='bt' ,cap='&2' ,sto=False ,t=-99,l=0,w=44)) # &2 1041 ,('srt2',d(tp='bt' ,cap='&3' ,sto=False ,t=-99,l=0,w=44)) # &3 1042 ,('srt3',d(tp='bt' ,cap='&4' ,sto=False ,t=-99,l=0,w=44)) # &4 1043 ,('srt4',d(tp='bt' ,cap='&5' ,sto=False ,t=-99,l=0,w=44)) # &5 1044 ,('srt5',d(tp='bt' ,cap='&6' ,sto=False ,t=-99,l=0,w=44)) # &6 1045 ,('srt6',d(tp='bt' ,cap='&7' ,sto=False ,t=-99,l=0,w=44)) # &7 1046 ,('srt-',d(tp='bt' ,cap='&9' ,sto=False ,t=-99,l=0,w=44)) # &9 1047 ,('cws-',d(tp='bt' ,cap='&W' ,sto=False ,t=-99,l=0,w=44)) # &w 1048 ,('cpnm',d(tp='bt' ,cap='&C' ,sto=False ,t=-99,l=0,w=44)) # &c 1049 ,('erpt',d(tp='bt' ,cap='&O' ,sto=False ,t=-99,l=0,w=44)) # &o 1050 ,('apnw',d(tp='bt' ,cap='&Y' ,sto=False ,t=-99,l=0,w=44)) # &y 1051 ,('help',d(tp='bt' ,cap='&H' ,sto=False ,t=-99,l=0,w=44)) # &h 1052 # Top-panel 1053 ,('ptop',d(tp='pn' ,h= 270 ,w=m.dlg_w ,ali=ALI_CL 1054 ,h_min=270 )) 1055 # Menu 1056 ,('menu',d(tp='bt' ,tid='cond' ,l=-40-5,w= 40 ,p='ptop' ,cap='&=' ,a='LR' )) # &= 1057 # Filter 1058 ,('chps',d(tp='bt' ,tid='cond' ,l=-270 ,r=-180 ,p='ptop' ,cap=_('+&Section…') ,hint=M.CHPS_H ,a='LR' )) # &s 1059 ,('flt_',d(tp='lb' ,tid='cond' ,l= 5 ,w= 70 ,p='ptop' ,cap='>'+M.FILTER_C+':' ,hint=M.FLTR_H )) # &f 1060 ,('cond',d(tp=co_tp,t= 5 ,l= 78 ,r=-270 ,p='ptop' ,items=m.cond_hl ,a='lR' )) # 1061#,('cond',d(tp='cb' ,t= 5 ,l= 78 ,r=-270 ,p='ptop' ,items=m.cond_hl ,a='lR' )) # 1062 # Table of keys+values 1063 ,('lvls',d(tp='lvw',t= 35,h=160,l= 5 ,r= -5 ,p='ptop' ,items=m.itms,cols=m.cols ,grid='1' ,a='tBlR' )) # 1064 # Editors for value 1065 ,('ed_s',d(tp='lb' ,t=210 ,l= 5 ,w= 70 ,p='ptop' ,cap=ed_s_c ,hint=m.cur_op ,a='TB' )) # &e 1066 ,('eded',d(tp='ed' ,tid='ed_s' ,l= 78 ,r=-270 ,p='ptop' ,vis=vis['eded'],ex0=not ens['eded'],a='TBlR' )) # 1067#,('eded',d(tp='ed' ,tid='ed_s' ,l= 78 ,r=-270 ,p='ptop' ,vis=vis['eded'],en=ens['eded'] ,a='TBlR' )) # 1068 ,('edcl',d(tp='clr',t=210-2 ,l= 210 ,r=-271 ,p='ptop' ,h=edit_h-4 ,vis=vis['edcl'],border=True ,a='TBlR' )) # 1069 ,('edcb',d(tp='cbr',tid='ed_s' ,l= 78 ,r=-270 ,p='ptop' ,items=its['edcb'] ,vis=vis['edcb'] ,a='TBlR' )) # 1070 ,('edrf',d(tp='ch' ,tid='ed_s' ,l= 78 ,w= 60 ,p='ptop' ,cap=_('f&alse') ,vis=vis['edrf'] ,a='TB' )) # &a 1071 ,('edrt',d(tp='ch' ,tid='ed_s' ,l= 140 ,w= 60 ,p='ptop' ,cap=_('t&rue') ,vis=vis['edrt'] ,a='TB' )) # &r 1072 ,('brow',d(tp='bt' ,tid='ed_s' ,l=-270 ,w= 90 ,p='ptop' ,cap=_('&...') ,vis=vis['brow'] ,a='TBLR' )) # &. 1073 ,('toop',d(tp='bt' ,tid='ed_s' ,l=-270 ,w= 90 ,p='ptop' ,cap=_('&GoTo') ,vis=vis['toop'],hint=M.TOOP_H ,a='TBLR' )) # &g 1074 ,('opjs',d(tp='bt' ,tid='ed_s' ,l=-270 ,w= 90 ,p='ptop' ,cap=_('E&dit') ,vis=vis['opjs'],hint=M.OPME_H ,a='TBLR' )) # &d 1075 # View def-value 1076 ,('dfv_',d(tp='lb' ,tid='dfvl' ,l= 5 ,w= 70 ,p='ptop' ,cap=_('>Defa&ult:') ,hint=m.cur_op ,a='TB' )) # &u 1077#,('dfvl',d(tp='ed' ,t=235 ,l= 78 ,r=-270 ,p='ptop' ,en=False ,sto=False ,a='TBlR' )) # 1078 ,('dfvl',d(tp='ed' ,t=235 ,l= 78 ,r=-270 ,p='ptop' ,ex0=True ,sto=False ,a='TBlR' )) # 1079#,('dfvl',d(tp='ed' ,t=235 ,l= 78 ,r=-270 ,p='ptop' ,ro_mono_brd='1,0,1' ,sto=False ,a='TBlR' )) # 1080 ,('dfcl',d(tp='clr',t=235+1 ,l= 210 ,r=-271 ,p='ptop' ,h=edit_h-4 ,vis=vis['dfcl'],border=True ,a='TBlR' )) # 1081 ,('setd',d(tp='bt' ,tid='dfvl' ,l=-270 ,w= 90 ,p='ptop' ,cap=_('Rese&t') ,en=ens['setd'] ,a='TBLR' )) # &t 1082 # For lexer/file 1083#,('to__',d(tp='lb' ,tid='ed_s' ,l=-170 ,w= 30 ,p='ptop' ,cap=_('>For:') ,a='TBLR' )) # 1084 ,('to__',d(tp='lb' ,tid='ed_s' ,l=-165 ,w= 30 ,p='ptop' ,cap=_('For:') ,a='TBLR' )) # 1085 ,('tolx',d(tp='ch' ,tid='ed_s' ,l=-140 ,w= 70 ,p='ptop' ,cap=_('Le&xer') ,a='TBLR' )) # &x 1086 ,('tofi',d(tp='ch' ,tid='ed_s' ,l=- 90 ,w= 70 ,p='ptop' ,cap=_('F&ile') ,hint=tofi_c ,en=tofi_en ,a='TBLR' )) # &i 1087 ,('lexr',d(tp='cbr',tid='dfvl' ,l=-165 ,w= 160 ,p='ptop' ,items=m.lexr_w_l ,a='TBLR' )) 1088 # Comment 1089 ,('cmsp',d(tp='sp' ,y=cmnt_t-5 ,ali=ALI_BT,sp_lr=5 )) 1090 ,('cmnt',d(tp='me' ,t=cmnt_t ,h= m.h_cmnt 1091 ,h_min=M.CMNT_MHT ,ali=ALI_BT,sp_lrb=5 ,ro_mono_brd='1,1,1' )) 1092 ,('stbr',d(tp='sb' ,y=-M.STBR_H 1093 ,h= M.STBR_H ,ali=ALI_BT )) 1094 ][1:] 1095 if 'mac'==get_desktop_environment(): 1096 cnts = [(cid,cnt) for cid,cnt in cnts if cnt.get('cap', '')[:3]!='srt'] 1097 cnts = odict(cnts) 1098 if m.how.get('hide_fil', False): 1099 for cid in ('tofi',): 1100 cnts[cid]['vis'] = False 1101 if m.how.get('hide_lex_fil', False): 1102 for cid in ('to__', 'tolx', 'lexr', 'tofi'): 1103 cnts[cid]['vis'] = False 1104 for cnt in cnts.values(): 1105 if 'l' in cnt: cnt['l'] = m.dlg_w+cnt['l'] if cnt['l']<0 else cnt['l'] 1106 if 'r' in cnt: cnt['r'] = m.dlg_w+cnt['r'] if cnt['r']<0 else cnt['r'] 1107 if 'y' in cnt: cnt['y'] = m.dlg_h+cnt['y'] if cnt['y']<0 else cnt['y'] 1108 1109 cnts['menu']['call'] = m.do_menu 1110 cnts['chps']['call'] = m.do_menu 1111 cnts['cpnm']['call'] = m.do_menu 1112 cnts['erpt']['call'] = m.do_menu 1113 cnts['apnw']['call'] = m.do_menu 1114 cnts['flt-']['call'] = m.do_fltr 1115 cnts['fltr']['call'] = m.do_fltr 1116 if m.live_fltr: 1117 cnts['cond']['call'] = m.do_fltr 1118 cnts['lexr']['call'] = m.do_lxfi 1119 cnts['tolx']['call'] = m.do_lxfi 1120 cnts['tofi']['call'] = m.do_lxfi 1121 cnts['lvls']['call'] = m.do_sele 1122 cnts['lvls']['on_click_header'] = m.do_sort 1123 cnts['srt0']['call'] = m.do_sort 1124 cnts['srt1']['call'] = m.do_sort 1125 cnts['srt2']['call'] = m.do_sort 1126 cnts['srt3']['call'] = m.do_sort 1127 cnts['srt4']['call'] = m.do_sort 1128 cnts['srt5']['call'] = m.do_sort 1129 cnts['srt6']['call'] = m.do_sort 1130 cnts['srt-']['call'] = m.do_sort 1131 cnts['cmsp']['call'] = m.do_cust 1132 cnts['cws-']['call'] = m.do_cust 1133 cnts['lvls']['on_click_dbl'] = m.do_dbcl #lambda idd,idc,data:print('on dbl d=', data) 1134 cnts['setd']['call'] = m.do_setv 1135 cnts['edcb']['call'] = m.do_setv 1136 cnts['edrf']['call'] = m.do_setv 1137 cnts['edrt']['call'] = m.do_setv 1138 cnts['brow']['call'] = m.do_setv 1139 cnts['toop']['call'] = m.do_setv 1140 cnts['opjs']['call'] = m.do_setv 1141 cnts['help']['call'] = m.do_help 1142 return cnts 1143 #def get_cnts 1144 1145 def get_vals(self, what=''): 1146 M,m = self.__class__,self 1147 m.cur_in = m._prep_opt('key2ind') 1148 if not what or 'cur' in what: 1149 vis,ens,vas,its,bcl = m._prep_opt() 1150 if not what: 1151 # all 1152 return dict(cond=m.cond_s 1153 ,lvls=m.cur_in 1154 ,eded=vas['eded'] 1155 ,edcb=vas['edcb'] 1156 ,edrf=vas['edrf'] 1157 ,edrt=vas['edrt'] 1158 ,dfvl=vas['dfvl'] 1159 ,cmnt=vas['cmnt'] 1160 ,tolx=m.for_ulf=='l' 1161 ,tofi=m.for_ulf=='f' 1162 ,lexr=m.lexr_l.index(m.lexr) if m.lexr in m.lexr_l else -1 1163 ) 1164 if '+' in what: 1165 rsp = dict() 1166 if '+lvls' in what: 1167 rsp.update(dict( 1168 lvls=m.cur_in 1169 )) 1170 if '+cur' in what: 1171 rsp.update(dict( 1172 eded=vas['eded'] 1173 ,edcb=vas['edcb'] 1174 ,edrf=vas['edrf'] 1175 ,edrt=vas['edrt'] 1176 ,dfvl=vas['dfvl'] 1177 ,cmnt=vas['cmnt'] 1178 )) 1179 if '+inlxfi' in what: 1180 rsp.update(dict( 1181 tolx=m.for_ulf=='l' 1182 ,tofi=m.for_ulf=='f' 1183 )) 1184 pass; #LOG and log('rsp={}',(rsp)) 1185 return rsp 1186 1187 if what=='lvls': 1188 return dict(lvls=m.cur_in 1189 ) 1190 if what=='lvls-cur': 1191 return dict(lvls=m.cur_in 1192 ,eded=vas['eded'] 1193 ,edcb=vas['edcb'] 1194 ,edrf=vas['edrf'] 1195 ,edrt=vas['edrt'] 1196 ,dfvl=vas['dfvl'] 1197 ,cmnt=vas['cmnt'] 1198 ) 1199 if what=='cur': 1200 return dict(eded=vas['eded'] 1201 ,edcb=vas['edcb'] 1202 ,edrf=vas['edrf'] 1203 ,edrt=vas['edrt'] 1204 ,dfvl=vas['dfvl'] 1205 ,cmnt=vas['cmnt'] 1206 ) 1207 #def get_vals 1208 1209 def do_resize(self, ag): 1210 M,m = self.__class__,self 1211 m.stbr_act(M.STBR_MSG, '') 1212 f_w = ag.fattr('w') 1213 l_w = ag.cattr('lvls', 'w') 1214 pass; #LOG and log('f_w,l_w={}',(f_w,l_w)) 1215 if f_w < m.dlg_min_w: return [] # fake event 1216 1217 m.col_ws= [ci['wd'] for ci in m.ag.cattr('lvls', 'cols')] 1218 if f_w == m.dlg_min_w and m.col_ws!=M.COL_MWS: 1219 return m.do_cust('cws-', ag) 1220 1221 sum_ws = sum(m.col_ws) 1222 pass; #LOG and log('l_w,sum_ws={}',(l_w,sum_ws)) 1223 if sum_ws >= (l_w - M.COL_N - M.SCROLL_W):return [] # decrease dlg - need user choice 1224 1225 # Auto increase widths of def-val and user-val cols 1226 extra = int((l_w - M.COL_N - M.SCROLL_W - sum_ws)/2) 1227 pass; #LOG and log('extra={}',(extra)) 1228 pass; #LOG and log('m.col_ws={}',(m.col_ws)) 1229 m.col_ws[3] += extra 1230 m.col_ws[4] += extra 1231 pass; #LOG and log('m.col_ws={}',(m.col_ws)) 1232 return d(ctrls=m.get_cnts('+cols')) 1233 #def do_resize 1234 1235 def do_cust(self, aid, ag, data=''): 1236 M,m = self.__class__,self 1237 m.stbr_act(M.STBR_MSG, '') 1238 pass; #LOG and log('aid={}',(aid)) 1239 if False:pass 1240 elif aid=='cmsp': 1241 # Splitter moved 1242 sp_y = ag.cattr('cmsp', 'y') 1243 return [] 1244 ##?? 1245 1246 elif aid=='cws-': 1247 # Set def col widths 1248 m.col_ws = M.COL_MWS[:] 1249 m.stores.pop(m.subset+'col_ws', None) 1250 return d(ctrls=m.get_cnts('+cols')) 1251 1252 elif aid=='vali': 1253 if dlg_valign_consts(): 1254 return d(ctrls=m.get_cnts()) 1255 return [] 1256 1257 elif aid=='rslt': 1258 # Restore dlg/ctrls sizes 1259 fpr = ag.fattrs() 1260 layout = data 1261 m.col_ws = layout.get('col_ws', m.col_ws) 1262 cmnt_h = layout.get('cmnt_h', ag.cattr('cmnt', 'h')) 1263 dlg_h = layout.get('dlg_h' , fpr['h']) 1264 dlg_w = layout.get('dlg_w' , fpr['w']) 1265 return d(ctrls= 1266 m.get_cnts('+cols')+ 1267 [('cmnt', d(h=cmnt_h)) 1268 ,('stbr', d(y=dlg_h)) # Hack to push it at bottom (by Alex) 1269 ],form=d( 1270 h=dlg_h 1271 ,w=dlg_w 1272 )) 1273 elif aid=='svlt': 1274 # Save dlg/ctrls sizes 1275 m.col_ws = [ci['wd'] for ci in m.ag.cattr('lvls', 'cols')] 1276 layout = data 1277 fpr = ag.fattrs() 1278 layout['dlg_w'] = fpr['w'] 1279 layout['dlg_h'] = fpr['h'] 1280 layout['cmnt_h']= ag.cattr('cmnt', 'h') 1281 layout['col_ws']= m.col_ws 1282 #def do_cust 1283 1284 def do_menu(self, aid, ag, data=''): 1285 pass; #LOG and log('aid={}',(aid)) 1286 M,m = self.__class__,self 1287 m.stbr_act(M.STBR_MSG, '') 1288 1289 scam = app.app_proc(app.PROC_GET_KEYSTATE, '') 1290 if scam=='c' and aid=='menu': 1291 return m.do_cust('vali', ag) 1292 1293 def wnen_menu(ag, tag): 1294 pass; #LOG and log('tag={}',(tag)) 1295 if False:pass 1296 1297 elif tag[:3]=='ch:': 1298 return m.do_fltr('chps', ag, tag[3:]) 1299 1300 elif tag=='srt-': 1301 return m.do_sort('', ag, -1) 1302 elif tag[:3]=='srt': 1303 return m.do_sort('', ag, int(tag[3])) 1304 1305 elif tag=='cws-': 1306 return m.do_cust(tag, ag) 1307 elif tag=='vali': 1308 return m.do_cust(tag, ag) 1309 1310# elif tag=='lubk': 1311# if app.ID_OK != app.msg_box( 1312# _('Restore user settings from backup copy?') 1313# , app.MB_OKCANCEL+app.MB_ICONQUESTION): return [] 1314# return m.do_file('restore-user') 1315# elif tag=='llbk': 1316# if app.ID_OK != app.msg_box( 1317# f(_('Restore lexer "{}" settings from backup copy?'), m.lexr) 1318# , app.MB_OKCANCEL+app.MB_ICONQUESTION): return [] 1319# return m.do_file('restore-lexr') 1320# elif tag=='dobk': 1321# m.stores[m.subset+'bk_sets'] = m.bk_sets = not m.bk_sets 1322# return [] 1323 1324 # elif tag=='dfns': 1325 # m.col_ws = [ci['wd'] for ci in m.ag.cattr('lvls', 'cols')] 1326 # new_file = app.dlg_file(True, m.defn_path.name, str(m.defn_path.parent), 'JSONs|*.json') 1327 # if not new_file or not os.path.isfile(new_file): return [] 1328 # return m.do_file('set-dfns', new_file) 1329 elif tag=='full': 1330 m.col_ws = [ci['wd'] for ci in m.ag.cattr('lvls', 'cols')] 1331 m.all_ops = not m.all_ops 1332 m.opts_full = load_vals(m.opts_defn 1333 ,lexr_json='lexer '+m.lexr+'.json' 1334 ,user_json=m.how.get('stor_json', 'user.json') 1335 , ed_=m.ed, full=m.all_ops) 1336 m.cur_op = m.cur_op if m.cur_op in m.opts_full else '' 1337 m.do_file('build-chp-tree') 1338 m.stbr_act(M.STBR_ALL, len(m.opts_full)) 1339 return d(ctrls=odict(m.get_cnts('+lvls +cur'))) 1340 1341 if tag=='apex': 1342 m.apply_one = not m.apply_one 1343 m.stores[m.subset+'apply_one'] = m.apply_one 1344 if tag=='apnw': 1345 ed.cmd(cmds.cmd_OpsReloadAndApply) 1346 if tag=='aufi': 1347 m.auto4file = not m.auto4file 1348 m.stores[m.subset+'auto4file'] = m.auto4file 1349 1350 if tag=='lifl': 1351 m.stores[m.subset+'live_fltr'] = not m.stores.get(m.subset+'live_fltr' , False) 1352 M.restart = True 1353 M.restart_cond = ag.cval('cond') 1354 return None # Close dlg 1355 1356 elif tag=='cpnm': 1357 app.app_proc(app.PROC_SET_CLIP, m.cur_op) 1358 elif tag=='erpt': 1359 body = '\n'.join(m.chng_rpt) 1360 dlg_wrapper(_('Сhange log') , 500+10 ,400+10, 1361 [ dict(cid='body',tp='me' ,l=5,w=500 ,t=5,h=400, ro_mono_brd='1,0,0')] 1362 , dict(body=body), focus_cid='body') 1363 elif tag=='locv': 1364 # m.do_file('locate-opt') # while wait core fix 1365 if m.do_file('goto-opt'): return None # need close dlg 1366 elif tag=='locd': 1367 # m.do_file('locate-def') # while wait core fix 1368 if m.do_file('goto-def'): return None # need close dlg 1369 1370 elif tag[:4] in ('rslt', 'rmlt', 'svlt'): 1371 layouts_l = m.stores.get(m.subset+'layouts', []) # [{nm:Nm, dlg_h:H, dlg_w:W, ...}] 1372 layouts_d = {lt['nm']:lt for lt in layouts_l} 1373 lt_i = int(tag[4:]) if tag[:4] in ('rslt', 'rmlt') else -1 1374 layout = layouts_l[lt_i] if lt_i>=0 else None 1375 if 0:pass 1376 elif tag[:4]=='rmlt': 1377 if app.ID_OK != app.msg_box( 1378 f(_('Remove layout "{}"?'), layout['nm']) 1379 , app.MB_OKCANCEL+app.MB_ICONQUESTION): return [] 1380 del layouts_l[lt_i] 1381 elif tag=='svlt': 1382 nm_tmpl = _('#{}') 1383 layout_nm = f(nm_tmpl 1384 ,first_true(itertools.count(1+len(layouts_d)) 1385 ,pred=lambda n:f(nm_tmpl, n) not in layouts_d)) # First free #N after len() 1386 while True: 1387 pass; #LOG and log('layout_nm={!r}',(layout_nm)) 1388 layout_nm = app.dlg_input('Name to save current sizes of the dialog and controls', layout_nm) 1389 if not layout_nm: return [] 1390 layout_nm = layout_nm.strip() 1391 if not layout_nm: return [] 1392 if layout_nm in layouts_d and \ 1393 app.ID_OK != app.msg_box( 1394 f(_('Name "{}" already used. Overwrite?'), layout_nm) 1395 , app.MB_OKCANCEL+app.MB_ICONQUESTION): continue 1396 break 1397 layout = None 1398 if layout_nm in layouts_d: 1399 layout = layouts_d[layout_nm] # Overwrite 1400 else: 1401 layout = d(nm=layout_nm) # Create 1402 layouts_l+=[layout] 1403 # Fill 1404 m.do_cust( 'svlt', ag, layout) 1405 elif tag[:4]=='rslt': 1406 return m.do_cust('rslt', ag, layout) 1407 # Save 1408 m.stores[m.subset+'layouts'] = layouts_l 1409 return [] 1410 1411 elif tag=='rprt': 1412 m.do_file('out-rprt') 1413 elif tag=='help': 1414 return m.do_help('', ag) 1415 return [] 1416 #def wnen_menu 1417 pass; #LOG and log('',()) 1418 1419 if aid=='chps': 1420 def tree2menu(node, chp=''): 1421 mn_l = [ d( tag='ch:'+ node['path'] 1422 , cap=f('{} ({})', chp, len(node['ops'])) 1423 , cmd=wnen_menu) 1424 ,d( cap='-') 1425 ] if chp else [] 1426 for chp,kid in node['kids'].items(): 1427 mn_l +=([d( cap=f('{} ({})', chp, len(kid['ops'])) 1428 , sub=tree2menu(kid, chp)) 1429 ] 1430 if 'kids' in kid else 1431 [d( tag='ch:'+ kid['path'] 1432 , cap=f('{} ({})', chp, len(kid['ops'])) 1433 , cmd=wnen_menu) 1434 ] 1435 ) 1436 return mn_l 1437 #def tree2menu 1438 mn_its = tree2menu(m.chp_tree) 1439 ag.show_menu('chps', mn_its) 1440 1441 if aid=='apnw': return wnen_menu(ag, aid) 1442 if aid=='cpnm': return wnen_menu(ag, aid) 1443 if aid=='erpt': return wnen_menu(ag, aid) 1444 1445 if aid=='menu': 1446 locv_c = f(M.LOCV_C, m.cur_op) 1447 locd_c = f(M.LOCD_C, m.cur_op) 1448 lts_l = m.stores.get(m.subset+'layouts', []) # [{nm:Nm, dlg_h:H, dlg_w:W, ...}] 1449 full_en = not m.how.get('only_with_def', False) # Forbid to switch fo User+Lexer ops 1450 live_fltr=m.stores.get(m.subset+'live_fltr' , False) 1451 pass; #lts_l = [d(nm='Nm1'), d(nm='Nm2')] 1452 mn_its = \ 1453 [ d(tag='cpnm' ,cap=_('&Copy option name') ,key='Alt+C' 1454 ),d( cap='-' 1455 ),d( cap=_('&Layout') ,sub= 1456 [ d(tag='svlt' ,cap=_('&Save current layout...') 1457 ),d( cap='-' 1458 )]+ ( 1459 [ d(tag='rslt'+str(nlt) ,cap=f(_('Restore layout "{}"'), lt['nm'])) for nlt, lt in enumerate(lts_l) 1460 ]+ 1461 [ d( cap=_('&Forget layout'),sub= 1462 [ d(tag='rmlt'+str(nlt) ,cap=f(_('Forget layout "{}"...'), lt['nm'])) for nlt, lt in enumerate(lts_l) 1463 ]) 1464 ] if lts_l else []) + 1465 [ d( cap='-' 1466 ),d(tag='vali' ,cap=_('Adjust vertical alignments...') 1467 ),d(tag='cws-' ,cap=_('Set default columns &widths') ,key='Alt+W' 1468 )] 1469 ),d( cap=_('&Table') ,sub= 1470 [ d(tag='srt'+str(cn) ,cap=f(_('Sort by column "{}"'), cs.split()[0]) 1471 ,ch=sorts_on(m.sorts, cn) 1472 ,key='Alt+'+str(1+cn)) 1473 for cn, cs in enumerate(M.COL_NMS) 1474 ]+ 1475 [ d( cap='-' 1476 ),d(tag='srt-' ,cap=_('Reset sorting') ,key='Alt+9' 1477 )] 1478 ),d( cap=_('M&ore') ,sub= 1479 [ d(tag='locv' ,cap=locv_c ,en=bool(m.cur_op) 1480 ),d(tag='locd' ,cap=locd_c ,en=bool(m.cur_op) 1481 ),d( cap='-' 1482 ),d(tag='erpt' ,cap=_('Show rep&ort of changes...') ,key='Alt+O' 1483 ),d(tag='apex' ,cap=_('Apply changes on exit') ,ch=m.apply_one 1484 ),d(tag='apnw' ,cap=_('Appl&y changes now') ,en=m.apply_need ,key='Alt+Y' 1485 ),d(tag='aufi' ,cap=_('Auto-update FILE options') ,ch=m.auto4file 1486 ),d( cap='-' 1487 ),d(tag='lifl' ,cap=M.LIFL_C ,ch=live_fltr 1488 ),d( cap='-' 1489 ),d(tag='full' ,cap=M.FULL_C ,ch=m.all_ops ,en=full_en 1490 )] 1491 ),d( cap='-' 1492 ),d( tag='rprt' ,cap=_('Create HTML &report') 1493 ),d( cap='-' 1494 ),d( tag='help' ,cap=_('&Help...') ,key='Alt+H' 1495 )] 1496 pass; #LOG and log('mn_its=¶{}',pf(mn_its)) 1497 def add_cmd(its): 1498 for it in its: 1499 if 'sub' in it: add_cmd(it['sub']) 1500 else: it['cmd']=wnen_menu 1501 add_cmd(mn_its) 1502 ag.show_menu(aid, mn_its) 1503 return [] 1504 #def do_menu 1505 1506 def do_fltr(self, aid, ag, data=''): 1507 M,m = self.__class__,self 1508 m.stbr_act(M.STBR_MSG, '') 1509 m.col_ws= [ci['wd'] for ci in m.ag.cattr('lvls', 'cols')] 1510 1511 fid = ag.fattr('fid') 1512 pass; #LOG and log('aid,fid={}',(aid,fid)) 1513 if aid=='fltr' and fid in ('dfvl', 'eded', 'edrf', 'edrt'): 1514 # Imitate default button 1515 return m.do_setv('setd' if fid in ('dfvl',) else 1516 'setv' if fid in ('eded',) else 1517 fid if fid in ('edrf', 'edrt') else 1518 '' 1519 , ag) 1520 1521 if aid=='cond': 1522 pass; #LOG and log('ag.cval(cond)={}',(ag.cval('cond'))) 1523 m.cond_s = ag.cval('cond') 1524 fid = '' if m.live_fltr else 'lvls' 1525 if aid=='fltr': 1526 m.cond_s = ag.cval('cond') 1527 m.cond_hl = add_to_history(m.cond_s, m.cond_hl) if m.cond_s and not m.live_fltr else m.cond_hl 1528 fid = 'lvls' 1529 if aid=='flt-': 1530 m.cond_s = '' 1531 fid = 'cond' 1532 1533 if aid=='chps': 1534 # Append selected chapter as filter value 1535 scam = app.app_proc(app.PROC_GET_KEYSTATE, '') 1536 path = '@'+data 1537 if path not in m.cond_s: 1538 if scam!='c': 1539 m.cond_s= re.sub(r'@([\w/]*)', '', m.cond_s).strip() # del old 1540 m.cond_s = (m.cond_s+' '+path).strip() # add new 1541 m.cond_hl = add_to_history(m.cond_s, m.cond_hl) if not m.live_fltr else m.cond_hl 1542 fid = 'cond' 1543 1544 # Select old/new op 1545 m.cur_op= m._prep_opt('ind2key') 1546 ctrls = m.get_cnts('+lvls') 1547 m.cur_in= m._prep_opt('key2ind') 1548 if m.cur_in<0 and m.SKWULFs: 1549 # Sel top if old hidden 1550 m.cur_in= 0 1551 m.cur_op= m._prep_opt('ind2key', ind=m.cur_in) 1552 return d(ctrls=m.get_cnts('+cond =lvls +cur') 1553 ,vals =m.get_vals() 1554 ,form =d(fid=fid) 1555 ) 1556 1557 #def do_fltr 1558 1559 def do_sort(self, aid, ag, col=-1): 1560 scam = app.app_proc(app.PROC_GET_KEYSTATE, '') 1561 pass; #LOG and log('col,scam={}',(col,scam)) 1562 pass; #return [] 1563 M,m = self.__class__,self 1564 m.stbr_act(M.STBR_MSG, '') 1565 m.col_ws= [ci['wd'] for ci in m.ag.cattr('lvls', 'cols')] 1566 1567 if aid=='srt-' or col==-1: 1568 m.sorts = sorts_dflt(len(M.COL_NMS)) 1569 else: 1570 col = int(aid[3]) if aid[:3]=='srt' else col 1571 pass; #LOG and log('?? m.sorts={}',(m.sorts)) 1572 m.sorts = sorts_turn(m.sorts, col, scam) 1573 pass; #LOG and log('ok m.sorts={}',(m.sorts)) 1574 1575 old_in = m._prep_opt('key2ind') 1576 ctrls = m.get_cnts('+lvls') 1577 if old_in==0: 1578 # Set top if old was top 1579 m.cur_in= 0 1580 m.cur_op= m._prep_opt('ind2key', ind=m.cur_in) 1581 else: 1582 # Save old op 1583 m.cur_in= m._prep_opt('key2ind') 1584 return d(ctrls=m.get_cnts('=lvls +cur') 1585 ,vals =m.get_vals() 1586 ) 1587 #def do_sort 1588 1589 def do_sele(self, aid, ag, data=''): 1590 M,m = self.__class__,self 1591 m.stbr_act(M.STBR_MSG, '') 1592 pass; #LOG and log('data,m.cur_op,m.cur_in={}',(data,m.cur_op,m.cur_in)) 1593 m.cur_op= m._prep_opt('ind2key') 1594 pass; #LOG and log('m.cur_op,m.cur_in={}',(m.cur_op,m.cur_in)) 1595 pass; #log('###m.get_cnts(+cur)={}',(m.get_cnts('+cur'))) 1596 return d(ctrls=odict(m.get_cnts('+cur')) 1597 ,vals = m.get_vals('cur') 1598 ) 1599 #def do_sele 1600 1601 def do_lxfi(self, aid, ag, data=''): 1602 M,m = self.__class__,self 1603 m.stbr_act(M.STBR_MSG, '') 1604 pass; #LOG and log('aid={}',(aid)) 1605 m.col_ws= [ci['wd'] for ci in m.ag.cattr('lvls', 'cols')] 1606 1607 if False:pass 1608 elif aid in ('tolx', 'tofi'): 1609 # Changed "For Lexer/File" 1610 m.for_ulf = 'l' if aid=='tolx' and ag.cval('tolx') else \ 1611 'f' if aid=='tofi' and ag.cval('tofi') else \ 1612 'u' 1613 fid = 'lexr' \ 1614 if m.for_ulf=='l' and m.lexr not in m.lexr_l else \ 1615 m._prep_opt('fid4ed') 1616 return d(ctrls=m.get_cnts('+cur') 1617 ,vals =m.get_vals('+cur+inlxfi') 1618 ,form =d(fid=fid) 1619 ) 1620 elif aid=='lexr': 1621 # Change current lexer 1622 lexr_n = ag.cval('lexr') 1623 m.lexr = m.lexr_l[lexr_n] if lexr_n>=0 else '' 1624 m.cur_op= m._prep_opt('ind2key') 1625 m.do_file('load-data') 1626 ctrls = m.get_cnts('+lvls') 1627 m.cur_in= m._prep_opt('key2ind') 1628 if m.cur_in<0 and m.SKWULFs: 1629 # Sel top if old hidden 1630 m.cur_in= 0 1631 m.cur_op= m._prep_opt('ind2key', ind=m.cur_in) 1632 elif m.cur_in<0: 1633 m.cur_op= '' 1634 return d(ctrls=m.get_cnts('=lvls +cur') 1635 ,vals =m.get_vals()#'+lvls +cur') 1636 ) 1637 #def do_lxfi 1638 1639 def do_dbcl(self, aid, ag, data=''): 1640 M,m = self.__class__,self 1641 m.stbr_act(M.STBR_MSG, '') 1642 pass; #LOG and log('data,m.cur_op,m.cur_in={}',(data,m.cur_op,m.cur_in)) 1643 m.col_ws= [ci['wd'] for ci in m.ag.cattr('lvls', 'cols')] 1644 1645 if aid!='lvls': return [] 1646 # Dbl-click on lvls cell 1647 if sum(m.col_ws) > ag.cattr('lvls', 'w') - M.SCROLL_W: 1648 # Has hor-scrolling 1649 pass; #LOG and log('skip as h-scroll',()) 1650 return [] 1651 op_r = ag.cval('lvls') 1652 op_c = next(filter( # next(filter())==first_true 1653 lambda col_n_sw: col_n_sw[1]>data[0] # > x from click (x,y) 1654 , enumerate(accumulate(m.col_ws)) # (n_col, sum(col<=n)) 1655 ), [-1, -1 1656 ])[0] 1657 pass; #LOG and log('op_r,op_c,m.cur_op,m.cur_in={}',(op_r,op_c,m.cur_op,m.cur_in)) 1658 pass; #LOG and log('op_r,op_c={}',(op_r,op_c)) 1659 if False:pass 1660 elif op_c not in (M.COL_DEF,M.COL_USR,M.COL_LXR,M.COL_FIL): 1661 return [] 1662 elif -1==op_r: 1663 pass; #LOG and log('skip as no opt',()) 1664 return [] 1665 elif -1==op_c: 1666 pass; #LOG and log('skip as miss col',()) 1667 return [] 1668 elif M.COL_DEF==op_c: 1669 return d(form =d(fid='setd')) 1670 elif M.COL_USR==op_c and m.for_ulf!='u': 1671 # Switch to user vals 1672 m.for_ulf = 'u' 1673 elif M.COL_LXR==op_c and m.for_ulf!='l': 1674 # Switch to lexer vals 1675 m.for_ulf = 'l' 1676 elif M.COL_FIL==op_c and m.for_ulf!='f': 1677 # Switch to lexer vals 1678 m.for_ulf = 'f' 1679 else: 1680 return [] 1681 pass; LOG and log('op_r,op_c,m.for_ulf={}',(op_r,op_c,m.for_ulf)) 1682 return d(ctrls=m.get_cnts('+cur') 1683 ,vals =m.get_vals('+cur+inlxfi') 1684 ,form =d(fid=m._prep_opt('fid4ed')) 1685 ) 1686 #def do_dbcl 1687 1688 def do_setv(self, aid, ag, data=''): 1689 M,m = self.__class__,self 1690 m.stbr_act(M.STBR_MSG, '') 1691 pass; #LOG and log('aid,m.cur_op={}',(aid,m.cur_op)) 1692 if not m.cur_op: return [] 1693 m.col_ws= [ci['wd'] for ci in m.ag.cattr('lvls', 'cols')] 1694 1695 if aid=='toop': 1696# m.do_file('locate-opt') # while wait core fix 1697 if m.do_file('goto-opt'): return None # need close dlg 1698 return [] 1699 1700 trg = 'lexer '+m.lexr+'.json' if m.for_ulf=='l' else 'user.join' 1701 key4v = m.for_ulf+'val' 1702 op = m.cur_op 1703 oi = m.opts_full[op] 1704 frm = oi['frm'] 1705# if frm=='json': 1706# m.stbr_act(M.STBR_MSG, f(_('Edit {!r} to change value'), trg)) 1707# return [] 1708 dval = oi.get( 'def') 1709 uval = oi.get('uval') 1710 lval = oi.get('lval') 1711 fval = oi.get('fval') 1712 ulfvl = oi.get(key4v ) #fval if m.for_ulf=='f' else lval if m.for_ulf=='l' else uval 1713 jval = oi['jlvl'] if m.for_ulf=='l' else \ 1714 oi['juvl'] if m.for_ulf=='u' else \ 1715 oi['jfvl'] 1716 scam = app.app_proc(app.PROC_GET_KEYSTATE, '') 1717 1718 # Get new value 1719 newv = None 1720 erpt_s = '' 1721 if False:pass 1722 1723 elif aid=='setd' and \ 1724 m.for_ulf=='f' and \ 1725 op in apx.OPT2PROP: 1726 # Remove from file - set over def/user/lex val 1727 newv = oi.get('lval', oi.get('uval', oi.get('def'))) 1728 if newv==ulfvl: 1729 m.stbr_act(M.STBR_MSG, _('No need changes')) 1730 return [] 1731 erpt_s = 'reset-f' 1732 m.ed.set_prop(apx.OPT2PROP[op], newv) 1733 1734 elif aid=='setd' and \ 1735 ulfvl is not None and \ 1736 m.for_ulf!='f': 1737 # Remove from user/lexer 1738 if scam!='c' and \ 1739 app.ID_OK != app.msg_box(f(_('Remove {} option' 1740 '\n {} = {!r}' 1741 '\n?'), 'LEXER' if m.for_ulf=='l' else 'USER', op, jval) 1742 , app.MB_OKCANCEL+app.MB_ICONQUESTION): return [] 1743 newv= None 1744 1745 elif aid=='brow' and frm in ('hotk', 'file', '#rgb', '#rgb-e'): 1746 ulfvl_s = '' if ulfvl is None else ulfvl 1747 m.stbr_act(M.STBR_MSG, f(_('Default value: "{}". Old value: "{}"'), dval, ulfvl_s)) 1748 if frm in ('#rgb', '#rgb-e'): 1749 ulfvl_s = ulfvl_s if ulfvl_s else dval if frm=='#rgb' else '#fff' 1750 newv = app.dlg_color(apx.html_color_to_int(ulfvl_s)) 1751 if newv is None: return [] 1752 newv = apx.int_to_html_color(newv) 1753 else: 1754 newv= (app.dlg_hotkey(op) if frm=='hotk' else 1755 app.dlg_file(False, '', os.path.expanduser(ulfvl_s), '') if frm=='file' else None) 1756 m.stbr_act(M.STBR_MSG, '') 1757 if not newv: return [] 1758 1759 elif aid=='opjs': 1760 newv = edit_json_as_dict(op, ulfvl, dval, oi.get('cmt' , '')) 1761 if newv is None: return [] 1762 1763 elif aid=='setv': # Add/Set opt for user/lexer/file 1764 # Enter from edit. Need parse some string 1765 newv = m.ag.cval('eded') 1766 try: 1767 newv = int(newv) if frm=='int' else \ 1768 float(newv) if frm=='float' else \ 1769 newv 1770 except Exception as ex: 1771 app.msg_box(f(_('Incorrect value. It\'s needed in format: {}'), frm) 1772 , app.MB_OK+app.MB_ICONWARNING) 1773 return d(form=d(fid='eded')) 1774 if frm=='#rgb' or frm=='#rgb-e' and newv: # Testing new val 1775 try: 1776 apx.html_color_to_int(newv) 1777 except Exception as ex: 1778 app.msg_box(f(_('Incorrect value. It\'s needed in format: {}'), '#RGB or #RRGGBB') 1779 , app.MB_OK+app.MB_ICONWARNING) 1780 return d(form=d(fid='eded')) 1781 elif aid in ('edrf', 'edrt'): # Add/Set opt for user/lexer/file 1782 newv = aid=='edrt' 1783 newv = not newv if newv==ulfvl else newv 1784 elif aid=='edcb': # Add/Set opt into user/lexer/file 1785 pass; #LOG and log('oi={}',(oi)) 1786 vl_l = [k for k,v in oi.get('dct', [])] if 'dct' in oi else oi.get('lst', []) 1787 pass; #LOG and log('vl_l={}',(vl_l)) 1788 pass; #LOG and log('m.ag.cval(edcb)={}',(m.ag.cval('edcb'))) 1789 newv = vl_l[m.ag.cval('edcb')] 1790 pass; #LOG and log('newv={}',(newv)) 1791 1792 # Use new value to change env 1793 if newv is not None and newv==ulfvl: 1794 m.stbr_act(M.STBR_MSG, _('No need changes')) 1795 return [] 1796 1797 if m.for_ulf=='f' and newv is not None and op in apx.OPT2PROP: 1798 # Change for file 1799 erpt_s = 'set-f' 1800 ed.set_prop(apx.OPT2PROP[op], newv) 1801 1802 if m.for_ulf!='f': 1803 # Change target file 1804 pass; #LOG and log('?? do_erpt',()) 1805 erpt_s =('reset-u' if newv is None and m.for_ulf=='u' else 1806 'reset-l' if newv is None and m.for_ulf=='l' else 1807 'add-u' if ulfvl is None and m.for_ulf=='u' else 1808 'add-l' if ulfvl is None and m.for_ulf=='l' else 1809 'set-u' if m.for_ulf=='u' else 1810 'set-l' if m.for_ulf=='l' else '') 1811 pass; #LOG and log('?? set_opt',()) 1812 apx.set_opt(op 1813 ,newv 1814 ,apx.CONFIG_LEV_LEX if m.for_ulf=='l' else apx.CONFIG_LEV_USER 1815 ,ed_cfg =None 1816 ,lexer =m.lexr if m.for_ulf=='l' else None 1817 ,user_json=m.how.get('stor_json', 'user.json') 1818 ) 1819 1820 if not m.apply_one: 1821 pass; #LOG and log('?? OpsReloadAndApply',()) 1822 ed.cmd(cmds.cmd_OpsReloadAndApply) 1823 else: 1824 m.apply_need = True 1825 1826 # Use new value to change dlg data 1827 pass; #LOG and log('?? oi={}',(oi)) 1828 pass; #LOG and log('?? m.opts_full={}',pf(m.opts_full)) 1829 if False:pass 1830 elif aid=='setd': 1831 oi.pop(key4v, None) if m.for_ulf!='f' else 0 1832 else: 1833 pass; #LOG and log('key4v, newv={}',(key4v, newv)) 1834 oi[key4v] = newv 1835 pass; #LOG and log('oi={}',(oi)) 1836 upd_cald_vals(m.opts_full) 1837 pass; #LOG and log('oi={}',(oi)) 1838 1839 jnewv = oi['jlvl'] if m.for_ulf=='l' else oi['juvl'] if m.for_ulf=='u' else oi['jfvl'] 1840 m.do_erpt(erpt_s, jnewv, jval) 1841 pass; #LOG and log('ok oi={}',(oi)) 1842 pass; #LOG and log('ok m.opts_full={}',pf(m.opts_full)) 1843 1844 pass; #LOG and log('?? get_cnts',()) 1845 1846 if m.for_ulf!='f' and m.auto4file and op in apx.OPT2PROP: 1847 # Change FILE to over 1848 newv = oi.get('lval', oi.get('uval', oi.get('def'))) 1849 if newv!=oi.get('fval'): 1850 erpt_s = 'reset-f' 1851 m.ed.set_prop(apx.OPT2PROP[op], newv) 1852 oi['fval'] = newv 1853 jval = oi['jfvl'] 1854 upd_cald_vals(m.opts_full) 1855 jnewv = oi['jfvl'] 1856 m.do_erpt('auset-f', jnewv, jval) 1857 1858 pass; #LOG and log('m.get_vals(lvls-cur)={}',(m.get_vals('lvls-cur'))) 1859 return d(ctrls=m.get_cnts('+lvls+cur') 1860 ,vals =m.get_vals('lvls-cur') 1861 ) 1862 #def do_setv 1863 1864 def do_erpt(self, what='', jnewv=None, joldv=None): 1865 pass; #LOG and log('what, newv={}',(what, newv)) 1866 M,m = self.__class__,self 1867 1868 if 0==len(m.chng_rpt): 1869 rpt = f('Starting to change options at {:%Y-%m-%d %H:%M:%S}', datetime.datetime.now()) 1870 m.chng_rpt += [rpt] 1871 1872 oi = m.opts_full[m.cur_op] 1873 oldv= None 1874 rpt = '' 1875 if 0:pass 1876 elif what=='reset-f': 1877 rpt = f(_('Set FILE option to overridden value {!r}') ,jnewv) 1878 elif what=='set-f': 1879 rpt = f(_('Set FILE option to {!r}') ,jnewv) 1880 elif what=='auset-f': 1881 rpt = f(_('Auto-set FILE option to overridden value {!r}') ,jnewv) 1882 elif what=='reset-l': 1883 rpt = f(_('Remove LEXER {!r} option') ,m.lexr ) 1884 elif what=='set-l': 1885 rpt = f(_('Set LEXER {!r} option to {!r}') ,m.lexr ,jnewv) 1886 elif what=='add-l': 1887 rpt = f(_('Add LEXER {!r} option {!r}') ,m.lexr ,jnewv) 1888 elif what=='reset-u': 1889 rpt = f(_('Remove USER option') ) 1890 elif what=='set-u': 1891 rpt = f(_('Set USER option to {!r}') ,jnewv) 1892 elif what=='add-u': 1893 rpt = f(_('Add USER option {!r}') ,jnewv) 1894 else: 1895 return 1896 rpt = f('{} (from {!r})', rpt, joldv) \ 1897 if what[:3]!='add' and joldv is not None else rpt 1898 rpt = rpt.replace('True', 'true').replace('False', 'false') 1899 rpt = m.cur_op + ': ' + rpt 1900 rpt = f('{}. ', len(m.chng_rpt)) + rpt 1901# print(rpt) 1902 m.stbr_act(M.STBR_MSG, rpt + _(' [Alt+O - all changes]')) 1903 m.chng_rpt += [rpt] 1904 #def do_erpt 1905 1906 def do_help(self, aid, ag, data=''): 1907 M,m = self.__class__,self 1908 m.stbr_act(M.STBR_MSG, '') 1909 pass; #LOG and log('',()) 1910 dlg_wrapper('Help' 1911 , 680+10, 500+10 1912 , [d(cid='body', tp='me', l=5, t=5, w=680, h=500, ro_mono_brd='1,1,0')] 1913 , d( body= #NOTE: help 1914 f( 1915 _( 'About "{fltr}"' 1916 '\r ' 1917 ) 1918 +M.FLTR_H+ 1919 _('\r ' 1920 '\rOther tips.' 1921 '\r • Use ENTER to filter table and to change or reset value.' 1922 '\r • Use double click on any cell in columns' 1923 '\r "{c_usr}"' 1924 '\r "{c_lxr}"' 1925 '\r "{c_fil}"' 1926 '\r to change "{in_lxr}" flag and to put focus on the value field.' 1927 '\r • Use double click on any cell in column' 1928 '\r "{c_def}"' 1929 '\r to put focus on "{reset}".' 1930 '\r • Clicking "{reset}" will ask for confirmation, for user/lexer options.' 1931 '\r Hold Ctrl key to skip this confirmation.' 1932 '\r • Click on a column header sorts data in the column.' 1933 '\r Alt+# (# is 1..8) sorts the N column (not on macOS).' 1934 '\r Alt+9 resets sorting (not on macOS).' 1935 '\r Click with Ctrl allows to sort by several columns.' 1936 '\r Clicking with Ctrl on already sorted column does 2-state loop (down, up).' 1937 '\r Clicking with Ctrl on already sorted column with maximal sorting index, ' 1938 '\r does 3-state loop (down, up, off).' 1939 '\r • Use option "{lifl}" to see instant update of the list after' 1940 '\r each changing in the filter field' 1941 '\r (otherwise you need to press Enter after changing).' 1942 '\r With this option, no history of the filter is kept' 1943 '\r (filter combobox has empty dropdown list).' 1944 '\r • If current list line is scrolled out of view, ' 1945 '\r you can still see the option name - in the tooltip' 1946 '\r of "User" (Lexer/File) label near the value field.' 1947 '\r • Tooltip shows file name (or tag name), when cursor hovers the checkbox "{tofi}".' 1948 '\r • Some plugins store their settings into user.json.' 1949 '\r So after a while, user.json contains options not present in default.json.' 1950 '\r To see all these keys, use option "{full}".' 1951 '\r • Values in table column "!"' 1952 '\r ! option is set in "user.json",' 1953 '\r !! option is set in "lexer NNN.json",' 1954 '\r !!! option is set for current file,' 1955 '\r L default value is from "settings_default/lexer NNN.json",' 1956 '\r + not CudaText standard option.' 1957 ) , c_usr=M.COL_NMS[M.COL_USR] 1958 , c_lxr=M.COL_NMS[M.COL_LXR] 1959 , c_fil=M.COL_NMS[M.COL_FIL].split()[0] 1960 , c_def=M.COL_NMS[M.COL_DEF] 1961 , fltr = ag.cattr('flt_', 'cap', live=False).replace('&', '').strip(':') 1962 , in_lxr=ag.cattr('tolx', 'cap', live=False).replace('&', '') 1963 , reset= ag.cattr('setd', 'cap', live=False).replace('&', '') 1964 , tofi = ag.cattr('tofi', 'cap', live=False).replace('&', '') 1965 , lifl = M.LIFL_C.replace('&', '') 1966 , full = M.FULL_C.replace('&', '') 1967 )) 1968 ) 1969 return [] 1970 #def do_help 1971 1972 restart = False 1973 restart_cond= None 1974 #class OptEdD 1975 1976 1977def edit_json_as_dict(op, uval, dval, cmnt4v): 1978 """ Allow user to edit JSON value 1979 """ 1980 pass; #log("op, uval, dval={}",(op, uval, dval)) 1981 newv = None 1982 def acts(aid, ag, data=''): 1983 nonlocal newv 1984 if False:pass 1985 elif aid=='defv': 1986 return d(vals=d(meme=json.dumps(dval, indent=2)),fid='meme') 1987 elif aid=='undo': 1988 return d(vals=d(meme=json.dumps(uval, indent=2)),fid='meme') 1989 elif aid in ('test', 'okok'): 1990 mejs = ag.cval('meme') 1991 pass; #log("mejs={!r}",(mejs)) 1992 try: 1993 jsvl = json.loads(mejs, object_pairs_hook=odict) 1994 except Exception as ex: 1995 warn = str(ex) + c10 + (c10.join('{:>3}|{}'.format(n+1, s.replace(' ','·')) 1996 for n,s in enumerate(mejs.split(c10)))) 1997 return d(vals=d(cmnt=warn),fid='meme') 1998# app.msg_box(str(ex) 1999# +c10+(c10.join('{:>3}|{}'.format(n+1, s.replace(' ','·')) 2000# for n,s in enumerate(mejs.split(c10)))) 2001# , app.MB_OK) 2002# return d(fid='meme') 2003 if aid=='okok': 2004 newv = jsvl 2005 return None # Close 2006 return d(vals=d(cmnt=cmnt4v),fid='meme') 2007 #def acts 2008 DlgAgent( 2009 form =dict(cap = f(_('Edit JSON option ({})'), op) 2010 ,resize = True 2011 ,w = 510 2012 ,h = 400 2013 ) 2014 , ctrls=[0 2015 ,('meme',d(tp='me' ,l= 5 ,w=500 ,t= 5 ,h=150 ,a='tBlR')) 2016 ,('cmnt',d(tp='me' ,l= 5 ,w=500 ,t=160 ,h=200 ,ro_mono_brd='1,1,1' ,a='TBlR')) 2017 ,('defv',d(tp='bt' ,l= 5 ,w=110 ,t=370 ,cap=_('Set &default') ,a='TB' ,call=acts ,en=(dval is not None))) 2018 ,('undo',d(tp='bt' ,l=120 ,w=110 ,t=370 ,cap=_('&Undo changes') ,a='TB' ,call=acts)) 2019 ,('test',d(tp='bt' ,l=285 ,w= 70 ,t=370 ,cap=_('Chec&k') ,a='TBLR' ,call=acts)) 2020 ,('cans',d(tp='bt' ,l=360 ,w= 70 ,t=370 ,cap=_('Cancel') ,a='TBLR' ,call=acts)) 2021 ,('okok',d(tp='bt' ,l=435 ,w= 70 ,t=370 ,cap=_('OK') ,a='TBLR' ,call=acts ,def_bt=True)) 2022 ][1:] 2023 , vals =dict(meme=json.dumps(uval, indent=2) 2024 ,cmnt=cmnt4v) 2025 , fid ='meme' 2026 ).show() 2027 return newv 2028 #def edit_json_as_dict 2029 2030 2031class Command: 2032 def dlg_cuda_options(self): 2033 while True: 2034 OptEdD.restart = False 2035 self._dlg_opt() 2036 if not OptEdD.restart: break 2037 #def dlg_cuda_options 2038 2039 def _dlg_opt(self): 2040 if app.app_api_version()<MIN_API_VER: return app.msg_status(_('Need update CudaText')) 2041 defs_json = apx.get_opt('dlg_cuda_options.defs_json', 'default.json') 2042 defs_json = defs_json if os.sep in defs_json else apx.get_def_setting_dir()+os.sep+defs_json 2043 OptEdD( 2044 path_keys_info=defs_json 2045 , subset='df.' 2046 ).show(_('CudaText options')) 2047 #def _dlg_opt 2048 #class Command 2049 2050def add_to_history(val:str, lst:list, max_len=MAX_HIST, unicase=False)->list: 2051 """ Add/Move val to list head. """ 2052 lst_u = [ s.upper() for s in lst] if unicase else lst 2053 val_u = val.upper() if unicase else val 2054 if val_u in lst_u: 2055 if 0 == lst_u.index(val_u): return lst 2056 del lst[lst_u.index(val_u)] 2057 lst.insert(0, val) 2058 if len(lst)>max_len: 2059 del lst[max_len:] 2060 return lst 2061 #def add_to_history 2062 2063RPT_HEAD = ''' 2064<html> 2065<head> 2066 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 2067 <title>CudaText options</title> 2068 <style type="text/css"> 2069td, th, body { 2070 color: #000; 2071 font-family: Verdana, Arial, Helvetica, sans-serif; 2072 font-size: 12px; 2073} 2074table { 2075 border-width: 1px; 2076 border-spacing: 2px; 2077 border-color: gray; 2078 border-collapse:collapse; 2079} 2080table td, table th{ 2081 border-width: 1px; 2082 padding: 1px; 2083 border-style: solid; 2084 border-color: gray; 2085} 2086pre { 2087 margin: 0; 2088 padding: 0; 2089} 2090td.nxt { 2091 color: grey; 2092 word-break: break-all; 2093} 2094td.win { 2095 font-weight: bold; 2096 word-break: break-all; 2097} 2098 </style> 2099</head> 2100<body> 2101''' 2102RPT_FOOT = ''' 2103</body> 2104</html> 2105''' 2106 2107def do_report(fn, lex='', ed_=ed): 2108 def hard_word_wrap(text, rmax): 2109 reShift = re.compile(r'\s*') 2110 reHeadTail = re.compile(r'(.{' + str(rmax) + r'}\S*)\s*(.*)') 2111 src_lines = text.splitlines() 2112 pass; #print('src_lines=',src_lines) 2113 trg_lines = [] 2114 for line in src_lines: 2115 pass; #print('line=', line, 'len=', len(line.rstrip())) 2116 if len(line.rstrip()) <= rmax: 2117 trg_lines.append(line) 2118 continue 2119 shift = reShift.match(line).group(0) 2120 head, \ 2121 tail = reHeadTail.match(line).group(1, 2) 2122 if not tail: 2123 tail= line.split()[-1] 2124 head= line[:-len(tail)] 2125 pass; #print('head=', head, 'tail=', tail) 2126 trg_lines.append(head) 2127 trg_lines.append(shift+tail) 2128 pass; #print('trg_lines=',trg_lines) 2129 return '\n'.join(trg_lines) 2130 #def hard_word_wrap 2131 2132# lex = ed_.get_prop(app.PROP_LEXER_CARET) 2133 def_json = apx.get_def_setting_dir() +os.sep+'default.json' 2134 usr_json = app.app_path(app.APP_DIR_SETTINGS)+os.sep+'user.json' 2135 lex_json = app.app_path(app.APP_DIR_SETTINGS)+os.sep+lex if lex else '' 2136 2137 def_opts = apx._get_file_opts(def_json, {}, object_pairs_hook=collections.OrderedDict) 2138 usr_opts = apx._get_file_opts(usr_json, {}, object_pairs_hook=collections.OrderedDict) 2139 lex_opts = apx._get_file_opts(lex_json, {}, object_pairs_hook=collections.OrderedDict) if lex else None 2140 2141 def_opts = pickle.loads(pickle.dumps(def_opts)) # clone to pop 2142 usr_opts = pickle.loads(pickle.dumps(usr_opts)) # clone to pop 2143 lex_opts = pickle.loads(pickle.dumps(lex_opts)) if lex else {} # clone to pop 2144 2145 fil_opts = {op:ed_.get_prop(pr) for op,pr in apx.OPT2PROP.items()} 2146# fil_opts = get_ovrd_ed_opts(ed) 2147 cmt_opts = {} 2148 # Find Commentary for def opts in def file 2149 # Rely: _commentary_ is some (0+) lines between opt-line and prev opt-line 2150 def_body = open(def_json).read() 2151 def_body = def_body.replace('\r\n', '\n').replace('\r', '\n') 2152 def_body = def_body[def_body.find('{')+1:] # Cut head with start '{' 2153 def_body = def_body.lstrip() 2154 for opt in def_opts.keys(): 2155 pos_opt = def_body.find('"{}"'.format(opt)) 2156 cmt = def_body[:pos_opt].strip() 2157 cmt = ('\n\n'+cmt).split('\n\n')[-1] 2158 cmt = re.sub('^\s*//', '', cmt, flags=re.M) 2159 cmt = cmt.strip() 2160 cmt_opts[opt] = html.escape(cmt) 2161 def_body= def_body[def_body.find('\n', pos_opt)+1:] # Cut the opt 2162 2163 with open(fn, 'w', encoding='utf8') as f: 2164 f.write(RPT_HEAD) 2165 f.write('<h4>High priority: editor options</h4>\n') 2166 f.write('<table>\n') 2167 f.write( '<tr>\n') 2168 f.write( '<th>Option name</th>\n') 2169 f.write( '<th>Value in<br>default</th>\n') 2170 f.write( '<th>Value in<br>user</th>\n') 2171 f.write( '<th>Value in<br>{}</th>\n'.format(lex)) if lex else None 2172 f.write( '<th title="{}">Value for file<br>{}</th>\n'.format(ed_.get_filename() 2173 , os.path.basename(ed_.get_filename()))) 2174 f.write( '<th>Comment</th>\n') 2175 f.write( '</tr>\n') 2176 for opt in fil_opts.keys(): 2177 winner = 'def' 2178 winner = 'usr' if opt in usr_opts else winner 2179 winner = 'lex' if opt in lex_opts else winner 2180 winner = 'fil' if opt in fil_opts else winner 2181 f.write( '<tr>\n') 2182 f.write( '<td>{}</td>\n'.format(opt)) 2183 f.write( '<td class="{}">{}</td>\n'.format('win' if winner=='def' else 'nxt', def_opts.get(opt, ''))) 2184 f.write( '<td class="{}">{}</td>\n'.format('win' if winner=='usr' else 'nxt', usr_opts.get(opt, ''))) 2185 f.write( '<td class="{}">{}</td>\n'.format('win' if winner=='lex' else 'nxt', lex_opts.get(opt, ''))) if lex else None 2186 f.write( '<td class="{}">{}</td>\n'.format('win' if winner=='fil' else 'nxt', fil_opts.get(opt, ''))) 2187# f.write( '<td><pre>{}</pre></td>\n'.format(cmt_opts.get(opt, ''))) 2188 f.write( '<td><pre>{}</pre></td>\n'.format(hard_word_wrap(cmt_opts.get(opt, ''), 50))) 2189 f.write( '</tr>\n') 2190 def_opts.pop(opt, None) 2191 usr_opts.pop(opt, None) 2192 lex_opts.pop(opt, None) if lex else None 2193 f.write('</table><br/>\n') 2194 f.write('<h4>Overridden default options</h4>\n') 2195 f.write('<table>\n') 2196 f.write( '<tr>\n') 2197 f.write( '<th width="15%">Option name</th>\n') 2198 f.write( '<th width="20%">Value in<br>default</th>\n') 2199 f.write( '<th width="20%">Value in<br>user</th>\n') 2200 f.write( '<th width="10%">Value in<br>{}<br></th>\n'.format(lex)) if lex else None 2201 f.write( '<th width="35%">Comment</th>\n') 2202 f.write( '</tr>\n') 2203 for opt in def_opts.keys(): 2204 winner = 'def' 2205 winner = 'usr' if opt in usr_opts else winner 2206 winner = 'lex' if opt in lex_opts else winner 2207 winner = 'fil' if opt in fil_opts else winner 2208 f.write( '<tr>\n') 2209 f.write( '<td>{}</td>\n'.format(opt)) 2210 f.write( '<td class="{}">{}</td>\n'.format('win' if winner=='def' else 'nxt', def_opts.get(opt, ''))) 2211 f.write( '<td class="{}">{}</td>\n'.format('win' if winner=='usr' else 'nxt', usr_opts.get(opt, ''))) 2212 f.write( '<td class="{}">{}</td>\n'.format('win' if winner=='lex' else 'nxt', lex_opts.get(opt, ''))) if lex else None 2213 f.write( '<td><pre>{}</pre></td>\n'.format(hard_word_wrap(cmt_opts.get(opt, ''), 50))) 2214 f.write( '</tr>\n') 2215 usr_opts.pop(opt, None) 2216 lex_opts.pop(opt, None) if lex else None 2217 f.write('</table><br/>\n') 2218 f.write('<h4>Overridden user-only options</h4>') 2219 f.write('<table>\n') 2220 f.write( '<tr>\n') 2221 f.write( '<th>Option name</th>\n') 2222 f.write( '<th>Value in<br>user</th>\n') 2223 f.write( '<th>Value in<br>{}</th>\n'.format(lex)) if lex else None 2224 f.write( '<th>Comment</th>\n') 2225 f.write( '</tr>\n') 2226 for opt in usr_opts.keys(): 2227 winner = 'usr' 2228 winner = 'lex' if opt in lex_opts else winner 2229 f.write( '<tr>\n') 2230 f.write( '<td>{}</td>\n'.format(opt)) 2231 f.write( '<td class="{}">{}</td>\n'.format('win' if winner=='usr' else 'nxt', usr_opts.get(opt, ''))) 2232 f.write( '<td class="{}">{}</td>\n'.format('win' if winner=='lex' else 'nxt', lex_opts.get(opt, ''))) if lex else None 2233 f.write( '<td><pre>{}</pre></td>\n'.format(cmt_opts.get(opt, ''))) 2234 f.write( '</tr>\n') 2235 lex_opts.pop(opt, None) if lex else None 2236 for opt in lex_opts: 2237 winner = 'lex' 2238 f.write( '<tr>\n') 2239 f.write( '<td>{}</td>\n'.format(opt)) 2240 f.write( '<td class="{}"></td> \n'.format('non')) 2241 f.write( '<td class="{}">{}</td>\n'.format('win', lex_opts.get(opt, ''))) 2242 f.write( '<td><pre>{}</pre></td>\n'.format(cmt_opts.get(opt, ''))) 2243 f.write( '</tr>\n') 2244 lex_opts.pop(opt, None) 2245 f.write('</table><br/>\n') 2246 f.write(RPT_FOOT) 2247 return True 2248 #def do_report(fn): 2249 2250def index_1(cllc, val, defans=-1): 2251 return cllc.index(val) if val in cllc else defans 2252 2253if __name__ == '__main__' : # Tests 2254 # To start the tests run in Console 2255 # exec(open(path_to_the_file, encoding="UTF-8").read()) 2256# app.app_log(app.LOG_CONSOLE_CLEAR, 'm') 2257# for smk in [smk for smk 2258# in sys.modules if 'cuda_options_editor.tests.test_options_editor' in smk]: 2259# del sys.modules[smk] # Avoid old module 2260# import cuda_options_editor.tests.test_options_editor 2261# import unittest 2262# suite = unittest.TestLoader().loadTestsFromModule( cuda_options_editor.tests.test_options_editor) 2263# unittest.TextTestRunner(verbosity=0).run(suite) 2264 2265 pass 2266 2267''' 2268ToDo 2269[+][kv-kv][02apr17] History for cond 2270[-][kv-kv][02apr17] ? Chapters list and "chap" attr into kinfo 2271[-][kv-kv][02apr17] ? Tags list and "tag" attr into kinfo 2272[-][kv-kv][02apr17] ? Delimiter row in table 2273[ ][kv-kv][02apr17] "Need restart" in Comments 2274[+][kv-kv][02apr17] ? Calc Format by Def_val 2275[ ][kv-kv][02apr17] int_mm for min+max 2276[+][kv-kv][02apr17] VERS in Title 2277[+][at-kv][02apr17] 'enum' вместо 'enum_i' 2278[ ][kv-kv][02apr17] Save top row in table 2279[+][kv-kv][03apr17] Show stat in Chap-combo and tags check-list 2280[-][kv-kv][03apr17] ? Add chap "(No chapter)" 2281[-][kv-kv][03apr17] ? Add tag "#no_tag" 2282[+][kv-kv][03apr17] Call opts report 2283[+][at-kv][04apr17] Format 'font' 2284[-][at-kv][04apr17] ? FilterListView 2285[+][at-kv][04apr17] use new default.json 2286[-][kv-kv][04apr17] Testing for update user.json 2287[+][kv-kv][04apr17] Restore Sec and Tags 2288[+][kv-kv][04apr17] ro-combo hitory for Tags 2289[+][kv-kv][05apr17] Add "default" to fonts if def_val=="default" 2290[+][at-kv][05apr17] Preview for format=fontmay 2291[+][kv-kv][06apr17] Spec filter sign: * - to show only modified 2292[-][kv-kv][06apr17] Format color 2293[+][kv-kv][24apr17] Sort as Def or as User 2294[+][kv-kv][05may17] New type "list of str" 2295[ ][kv-kv][23jun17] ? Filter with tag (part of tag?). "smth #my" 2296[+][kv-kv][15mar18] ? Filter with all text=key+comment 2297[+][kv-kv][19mar18] ? First "+" to filter with comment 2298[-][kv-kv][19mar18] !! Point the fact if value is overed in ed 2299[?][kv-kv][20mar18] Allow to add/remove opt in user/lex 2300[?][kv-kv][21mar18] ? Allow to meta keys in user.json: 2301 "_fif_LOG__comment":"Comment for fif_LOG" 2302[+][kv-kv][22mar18] Set control's tab_order to always work Alt+E for "Valu&e" 2303[ ][kv-kv][26mar18] Use 'editor' for comment 2304[+][kv-kv][26mar18] Increase w for one col when user increases w of dlg (if no h-scroll) 2305[+][kv-kv][13apr18] DClick on Def-col - focus to Reset 2306[-][kv-kv][16apr18] Open in tag for fmt=json 2307[?][kv-kv][23apr18] ? Show opt from cur line if ed(default.json) 2308[+][at-kv][03may18] Rework ask to confirm removing user/lex opt 2309[+][at-kv][04may18] Report to console all changes 2310[+][at-kv][05may18] Call OpsReloadAndApply 2311[+][kv-kv][05may18] Rework radio to checks (Linux bug: always set one of radio-buttons) 2312[-][kv-kv][05may18] Ask "Set also for current file?" if ops is ed.prop 2313[+][kv-kv][06may18] Menu command "Show changes" 2314[+][kv-kv][06may18] Show all file opt value. !!! only if val!=over-val 2315[+][kv-kv][06may18] Rework Sort 2316[+][kv-kv][14may18] Scale def col widths 2317[ ][at-kv][14may18] DClick over 1-2-3 is bad 2318[+][at-kv][14may18] Allow to refresh table on each changing of filter 2319[+][at-kv][15may18] Allow to extra sort cols with Ctrl+Click 2320[ ][kv-kv][04jun18] Cannot select section @Ui after selected @Ui/Tabs 2321[ ][kv-kv][16jun18] Have 2 filter control to instant and history. Switch by vis 2322[+][kv-kv][18jun18] More then one chap in filter. Append from menu if Ctrl holds 2323[+][at-kv][24apr19] Add types: rgb 2324[ ][at-kv][24apr19] Add type condition: int/float range 2325[+][kv-kv][25apr19] Hide cols "Lexer" and "File", controls []For and lexer list (by init opt) 2326[+][kv-kv][25apr19] Allow store other then user.json 2327[+][kv-kv][25apr19] Return 'was modified' from show() 2328'''