def onSelect(self, e): """ pognochange: means No change to the selected Pog. """ wx.BeginBusyCursor() self.indexselected = e.m_itemIndex pognochange = False if self.indexselected == self.lastindexselected: ## We have clicked on the same Pog as last time ## - ergo do nothing. pognochange = True # Record this for next time around self.lastindexselected = self.indexselected # Gets UNICODE !!! textpogname = self.GetItemText(self.indexselected) if self.whoami == "SOURCEPOG": ps.pub(source_pog_has_been_selected, textpogname, pognochange) else: ps.pub(target_pog_has_been_selected, textpogname, pognochange) self.toggle_list_icons_according_to_selection(pognochange) wx.EndBusyCursor()
def multiClick(self, e): ## NEW if e.GetId() == self.idnew: ## New Pog button pressed dlg = wx.TextEntryDialog(self, _("Enter a name for the new Pog"), _("New Pog"), _("Fonty Python")) dlg.SetValue("") if dlg.ShowModal() == wx.ID_OK: nam = dlg.GetValue() if nam == "": ps.pub( show_message, _( "A Pog with no name won't be created, "\ "however it was a good try!") ) ## Is it unique? elif fpsys.isPog(nam): ## Nope, it's there already ps.pub(show_message, _("\"{}\" already exists.").format(nam)) else: ## We have a winner. ## Make a pog object and then write it, ipog = fontcontrol.Pog(nam) try: ipog.write() except fontybugs.PogWriteError, e: ps.pub(show_error_and_abort, unicode(e)) del ipog ## Now put it into the list self.pogTargetlist.AddItem(nam) #ps.pub(add_item_to_notebook, nam) ps.pub(add_pog_item_to_source, nam) ps.pub(update_font_view) dlg.Destroy() return
def onClick(self, event) : """ Deals with clicks on self. Charmap button is a sub-test on a rect. This refreshes the underlying bitmap and DOES NOT cause the chain update_font_view (in gui_FontView) to run. """ if self.cmb_overout.truthstate and self.can_have_button(): self.openCharacterMap() return ## Prevent a click that would APPEND a ghost font. cannot_tick = fpsys.state.action == "APPEND" and self.fitem.badstyle == "FILE_NOT_FOUND" if cannot_tick: return if fpsys.state.cantick and not self.fitem.inactive: self.fitem.ticked = not(self.fitem.ticked) self.assemble_bitmap() # This only redraws a single font item. self.Refresh() #forces onPaint() ## Inc or dec a counter depending on the tickedness of this item if self.fitem.ticked: fpsys.state.numticks += 1 if not self.fitem.ticked: fpsys.state.numticks -= 1 ps.pub(toggle_main_button)
def ToggleMainButton(self): # both on, then off .. if ps.pub(toggle_selection_menu_item, True) self.button_main.Enable(True) self.button_main.SetLabel(self.buttMainLastLabel) #print "In ToggleMainButton, testing action:", fpsys.state.action if fpsys.state.action == "NOTHING_TO_DO": self.button_main.Enable(False) ps.pub(toggle_selection_menu_item, False) return if fpsys.state.numticks == 0: self.button_main.Enable(False)
def OnPogTargetClick(self, args): """ args[0] pogname args[1] is pognochange """ ## Made it so a second click on a target pog ## will unselect it. if args[1]: #pognochange = True, so let's deselect this pog self.SelectNoTargetPog() ps.pub(update_font_view) return try: fpsys.instantiateTargetPog(args[0]) except fontybugs.PogInvalid, e: ps.pub(show_error_and_abort, unicode(e)) return
def OnViewPogClick(self, args): """ args[0] is pogname, args[1] is pognochange """ # Dec 2017: Force no-selection in dir control # so the gui's visual "logic" makes sense: I.e we # are now looking at a Pog, not a Folder any more. self.tree.UnselectAll() ## Check pognochange, it means this is the same pog as last time. if args[1]: return ## instantiateViewPog calls pog.genList which bubbles: ## PogInvalid ## BUT - this error only makes sense from the ## cli pov. By the time the gui is running, that ## pog has been renamed .badpog and therefore ## won't even appear in the list. So, don't bother ## catching it. fpsys.instantiateViewPog(args[0]) if fpsys.state.samepogs: #forbid same pogs selection ps.pub(clear_targetpog_selection) else: ps.pub(reset_to_page_one) ps.pub(update_font_view)
def onMainClick(self, evt): """ Removes fonts, or Appends fonts. Depends on situation in fpsys.state """ #Saved by Robin Dunn, once again ! ! ! xPos, yPos = self.scrolledFontView.GetViewStart() wx.BeginBusyCursor() doupdate = False ## Let's determine what kind of thing to do: if fpsys.state.action == "REMOVE": ## We have a pog in viewobject and we must remove the ## selected fonts from it. vo = fpsys.state.viewobject victims = [] dowrite = False for fi in vo: if fi.ticked: victims.append(fi) #Put it into another list dowrite = True for fi in victims: vo.remove(fi) #Now remove it from the vo del victims if dowrite: fpsys.flushTicks() bug = False try: vo.write() except (fontybugs.PogWriteError), e: bug = True ps.pub(show_error, unicode(e)) doupdate = True if not bug: ps.pub(print_to_status_bar, _("Selected fonts have been removed.")) else: ps.pub(print_to_status_bar,_( "There was an error writing the pog "\ "to disk. Nothing has been done."))
class TargetPogChooser(wx.Panel): """ Chooses target pogs. Houses control buttons. """ def __init__(self, parent, id_zip_pog_button): wx.Panel.__init__(self, parent, id=-1) target_icon = fpwx.icon(self, 'icon_target') target_label = fpwx.large_label(self, _("Target Pogs")) s = None if fpsys.state.targetpattern == "P": s = fpsys.state.targetobject.name ## The actual list control self.pogTargetlist = PogChooser(self, whoami="TARGETPOG", select=s) ## Subscriptions: ps.sub(target_pog_has_been_selected, self.OnPogTargetClick) ##DND: class TargetPogChooser ps.sub(clear_targetpog_selection, self.SelectNoTargetPog) ##DND: class TargetPogChooser ## The "no pog" button self.idnone = wx.NewId() self.buttNoPog = wx.Button(self, label=_("Clear selection"), id=self.idnone) #self.buttNoPog = wx.BitmapButton(self, self.idnone, # fpwx.wxbmp( "icon_X" ) )#, style = wx.NO_BORDER) self.buttNoPog.SetToolTipString(_("Deselects any chosen Pogs.")) ## The buttons under the pog list ## Giving them all id numbers so my single handler can tell them apart. self.idnew = wx.NewId() self.idinstall = wx.NewId() self.iduninstall = wx.NewId() self.iddelete = wx.NewId() ## Buttons will be handled in wxgui, so we pass ids in __init__ self.idzip = id_zip_pog_button self.buttNew = wx.Button(self, label=_("New Pog"), id=self.idnew) self.buttNew.SetToolTipString(_("Creates a new, empty Pog")) self.buttInstall = wx.Button(self, label=_("Install Pog(s)"), id=self.idinstall) self.buttInstall.SetToolTipString( _("Installs all selected Pogs.\n{}").format( strings.ctrl_select_msg)) self.buttUninstall = wx.Button(self, label=_("Uninstall Pog(s)"), id=self.iduninstall) self.buttUninstall.SetToolTipString( _("Uninstalls all selected Pogs.\n{}").format( strings.ctrl_select_msg)) self.buttDelete = wx.Button(self, label=_("Delete Pog(s)"), id=self.iddelete) self.buttDelete.SetToolTipString(_("Deletes the selected Pog(s)")) self.buttZip = wx.Button(self, label=_("Zip Pog(s)"), id=self.idzip) self.buttZip.SetToolTipString( _("Save a zip file of the selected Pog(s)")) self.list_of_target_pogs_selected = None # will be used in wxgui mainvs = wx.BoxSizer(wx.VERTICAL) self.iconandtext = wx.BoxSizer(wx.HORIZONTAL) self.iconandtext.Add(target_icon, 0, wx.TOP | wx.BOTTOM | wx.LEFT, border=4) self.iconandtext.Add(target_label, 1, wx.LEFT | wx.BOTTOM | wx.ALIGN_BOTTOM, border=4) mainvs.Add(self.iconandtext, 0, wx.EXPAND) mainvs.Add(self.pogTargetlist, 1, wx.EXPAND) ## The buttons under the target: gs = wx.GridSizer(3, 2) gs.AddMany([ (self.buttNoPog, 1, wx.EXPAND), (self.buttZip, 1, wx.EXPAND), (self.buttInstall, 1, wx.EXPAND), (self.buttUninstall, 1, wx.EXPAND), (self.buttNew, 1, wx.EXPAND), (self.buttDelete, 1, wx.EXPAND), ]) mainvs.Add(gs, 0, wx.EXPAND) self.SetSizer(mainvs) ## Bind the events: e = wx.EVT_BUTTON # was wx.EVT_LEFT_UP self.buttNoPog.Bind(e, self.multiClick) self.buttNew.Bind(e, self.multiClick) self.buttInstall.Bind(e, self.multiClick) self.buttUninstall.Bind(e, self.multiClick) self.buttDelete.Bind(e, self.multiClick) self.buttZip.Bind(e, self.multiClick) self.toggleButtons() ## Catch all the button clicks on the control. def multiClick(self, e): ## NEW if e.GetId() == self.idnew: ## New Pog button pressed dlg = wx.TextEntryDialog(self, _("Enter a name for the new Pog"), _("New Pog"), _("Fonty Python")) dlg.SetValue("") if dlg.ShowModal() == wx.ID_OK: nam = dlg.GetValue() if nam == "": ps.pub( show_message, _( "A Pog with no name won't be created, "\ "however it was a good try!") ) ## Is it unique? elif fpsys.isPog(nam): ## Nope, it's there already ps.pub(show_message, _("\"{}\" already exists.").format(nam)) else: ## We have a winner. ## Make a pog object and then write it, ipog = fontcontrol.Pog(nam) try: ipog.write() except fontybugs.PogWriteError, e: ps.pub(show_error_and_abort, unicode(e)) del ipog ## Now put it into the list self.pogTargetlist.AddItem(nam) #ps.pub(add_item_to_notebook, nam) ps.pub(add_pog_item_to_source, nam) ps.pub(update_font_view) dlg.Destroy() return ## A list of multiple-selected pog names: tl = self.pogTargetlist multipogs = [ tl.GetItemText(i) for i in xrange(tl.GetItemCount()) if tl.IsSelected(i) ] ## DELETE if e.GetId() == self.iddelete: ## Selected Pog(s) to be deleted: tokill = multipogs iPogsToKill = [] ##Is any one of those installed? allok = True for p in tokill: ## Michael Hoeft tested pog delete and noticed the bug. Thanks. ## Fixed Oct 2009 iPog = fontcontrol.Pog(p) if iPog.isInstalled(): ps.pub( show_error, _( "One or more selected Pogs is installed, " \ "fix your selection and try again.") ) allok = False break iPogsToKill.append(iPog) if allok: ## Build a string for reporting in the dialog if len(tokill) > 1: pogname = "" for f in tokill[:-1]: pogname += u'"{}", '.format(f) #We want "remove "blah", "bloop". Are you sure?" pogname = u'{} "{}"'.format(pogname, tokill[-1]) else: pogname = u'"{}"'.format(tokill[0]) dlg = wx.MessageDialog( self, _("Remove {}. Are you sure?").format(pogname), _("Are you sure?"), wx.YES_NO | wx.ICON_INFORMATION) if dlg.ShowModal() == wx.ID_YES: ## Let's do them in: for victim in iPogsToKill: #tokill: ## Now kill the file on disk: try: victim.delete() except fontybugs.PogCannotDelete, e: ps.pub(show_error, unicode(e)) return ## Remove from the lists: self.pogTargetlist.RemoveItem(victim.name) ps.pub(remove_pog_item_from_source, victim.name) ## What if it was ALSO our view object? if fpsys.state.viewobject.label() == victim.name: ## It was! We must declare it Empty. fpsys.SetViewPogToEmpty() ## Now re-draw things ps.pub(update_font_view) dlg.Destroy() ## Select no pog. self.SelectNoTargetPog() return
fpsys.SetViewPogToEmpty() ## Now re-draw things ps.pub(update_font_view) dlg.Destroy() ## Select no pog. self.SelectNoTargetPog() return ## NO POG pressed if e.GetId() == self.idnone: ## Select No Pog button pressed if fpsys.state.targetobject is None: return #Already done. self.SelectNoTargetPog() ps.pub(update_font_view) return #No need to tell mainframe about this. ## Prepare for Install/Uninstall POG ## install or uninstall all selected pogs - caters for multiple pog selections ## the instantiateTargetPog must be called on each pog-name in the list ## to setup the globals in fpsys. This is new from my previous one-selection only ## code which assumed that instantiateTargetPog had been called already (when pog ## was selected by the mouse) ## Install if e.GetId() == self.idinstall: wx.BeginBusyCursor() for p in multipogs: fpsys.instantiateTargetPog(
def MainFontViewUpdate(self): """ Vital routine - the heart if the app. This decides what to do based on what has been selected. It draws the controls and the fonts as appropriate. It also sets flags in fpsys.state """ ## If any DismissablePanels are open, hide them: ps.pub(ensure_fontview_shown) ## Get shorter vars to use. V = fpsys.state.viewobject T = fpsys.state.targetobject VC = fpsys.state.viewpattern # View Char TC = fpsys.state.targetpattern # Target Char P = VC + TC # a two-char flag ## Okay, let's make a key:.. ## [S][rw][T][rws] <-- key's basic shape ## Where S is Source, T is Target: ## E == Empty View/Source - no fonts in chosen Source. ## N == Empty Target - no fonts. ## P is Pog ## F is Folder ## And rw: ## r is read (i.e. only can view) ## w is write (i.e. remove or append) ## s is same (S/T pogs are the same) ## Example: ## PrN -> Source pog+read. No target pog. ## i.e: We are viewing a pog that is installed ## (no write.) ## FPw -> Source folder. Target pog+write ## i.e. We are viewing a folder and have a Pog ## which is *not* installed, so can be 'written' ## i.e. have fonts appended. # EN -> EN (Empty source, No target) # EP -> EP (Empty source, Pog target (weird) ) # FN -> FN (Folder source, No target) if P in ('EN', 'EP', 'FN'): key = P # PN -> PrN or PwN # Pog source, No target. # Because 1) source is a Pog # and 2) there's no target, # the Pog can be: # r = view it only (i.e. it's installed) # w = remove fonts elif P == "PN": key = "PrN" if V.isInstalled() else "PwN" # FP -> FPr or FPw # Folder source, Pog target. Target r or w. # Similar logic. If target pog is installed # it can't be added-to, i.e. it's 'r' only. # else it can be 'w' elif P == "FP": key = "FPr" if T.isInstalled() else "FPw" # PP -> PPr or PPw or PPs # Target pog is either 'r' or 'w' or ... # Target pog 's' means source and target are same. elif P == "PP": if fpsys.state.samepogs: key = "PPs" else: key = "PPr" if T.isInstalled() else "PPw" # Some kind of error else: print _("FontView state error: Pattern is \"{}\"").format(P) raise SystemExit ## ..and use it to fetch from dict self.lbl_d d = self.lbl_d[key] # Little func to replace substrings. def rep(s): if "VIEW" in s: s = s.format(VIEW=V.label()) if "TARGET" in s: s = s.format(TARGET=T.label()) return s lab = rep(d['lab']) ## Do we add extra text about recursing? rtest = d.get('rtest', None) if rtest: lab = lab.format(RT=rtest()) # yes ## using dict n_a_r_s nars = d['nars'] fpsys.state.cantick = nars['cantick'] fpsys.state.action = nars['action'] self.TICKMAP = nars['tmap'] ## Enable/Disable the Purge menu item ## Switch it off, then: if the view is a Pog ## and it's *not* installed, we can switch it on. ## (Because you can't purge an installed font.) ## This has nothing to do with whatever target ## may be selected. ps.pub(toggle_purge_menu_item, False) if VC == "P" and not V.isInstalled(): ps.pub(toggle_purge_menu_item, True) self.buttMainLastLabel = rep(nars['btext']) self.main_font_info_label.SetLabel(lab) self.main_font_info_label.Show() i = nars['info'] t = d.get('tip', "") st = rep(u"{} {}".format(i, t)) self.status_text.SetLabel(st) self.ToggleMainButton() fpsys.markInactive() self.filterAndPageThenCallCreateFitmaps()
class FontViewPanel(wx.Panel): """ Standalone visual control to select fonts. The Panel that holds the ScrolledFontView control as well as the buttons etc. below and the text above. """ def __init__(self, parent): wx.Panel.__init__(self, parent, id=-1) self.firstrun = True self.pageindex = 1 # I start here self.total_number_of_pages = 0 self.filter = "" self.TICKMAP = None self._TICK = fpwx.wxbmp('tick') self._CROSS = fpwx.wxbmp('cross') #Sept 2009 ## Bitmaps to be used in the Fitmap drawing. ## Fetched from there as dict items. It got weird. self.SEGFAULT = fpwx.wxbmp('font_segfault') self.NO_DRAW = fpwx.wxbmp('font_cannot_draw') self.NOT_FOUND = fpwx.wxbmp('font_not_found') self.INFO_ITEM = fpwx.wxbmp('font_info_item') self.TICKSMALL = fpwx.wxbmp('ticksmall') self.BUTTON_CHARMAP = fpwx.wxbmp('button_charmap') self.BUTTON_CHARMAP_OVER = fpwx.wxbmp('button_charmap_over') ## -- ## Dicts for use in state ## self._setup_state_dicts() ## ## START GUI ## === #icon_open_folder = fpwx.wxbmp("icon_open_folder") #icon_pog = fpwx.wxbmp("pog16x16") #icon_pog_installed = fpwx.wxbmp("pog16x16.installed") ## Sizer: to hold all the others main_view_sizer = wx.BoxSizer(wx.VERTICAL) ## Sizer: Icon and Main Label icon_and_text_sizer = wx.BoxSizer(wx.HORIZONTAL) ## Sizer: Filter and Pager controls filter_and_pager_sizer = wx.BoxSizer(wx.HORIZONTAL) ## Icon view_icon = fpwx.icon(self, 'icon_viewing') # Main heading self.main_font_info_label = fpwx.h1(self, u"..", ellip=wx.ST_ELLIPSIZE_END, Layout_func=self.Layout) icon_and_text_sizer.Add(view_icon, 0, wx.EXPAND | wx.ALIGN_CENTER_HORIZONTAL | wx.RIGHT, border=4) icon_and_text_sizer.Add(self.main_font_info_label, 1, wx.EXPAND | wx.TOP | wx.ALIGN_CENTER_HORIZONTAL, border=6) ## The status label self.status_text = fpwx.label(self, u"Subinfo", ellip=wx.ST_ELLIPSIZE_END, Layout_func=self.Layout) ## Quick search Bold Italic Regular buttons ## It occurs to me that these are English words... ## Do fonts contain i18n on styles? ## See: https://fontforge.github.io/fontstyles.html idBold = wx.NewId() idItalic = wx.NewId() self.idRegular = wx.NewId() self.BIR = { idBold: { 'style': "bold", 'truth': False, 'instance': None }, idItalic: { 'style': "italic", 'truth': False, 'instance': None }, self.idRegular: { 'style': "regular", 'truth': False, 'instance': None } } toggle_sizer = wx.BoxSizer(wx.HORIZONTAL) for idy, dic in self.BIR.iteritems(): bBIR = wx.ToggleButton(self, idy, size=(32, -1), style=wx.NO_BORDER) #Remarked label to show icons instead, label=dic['label']) bBIR.Bind(wx.EVT_TOGGLEBUTTON, self.onBIR) bmp = fpwx.wxbmp('icon_{}'.format(dic['style'])) bBIR.SetBitmap(bmp) bBIR.SetToolTipString(_("Filter {} fonts").format(dic['style'])) self.BIR[idy]['instance'] = bBIR toggle_sizer.Add(bBIR, 1, wx.EXPAND) filter_and_pager_sizer.Add(toggle_sizer, 1, wx.EXPAND) # Search box - has two callbacks self.search_filter = SearchFilter( self, search_func=self.do_search, cancel_func=self.on_clear_button_click) self.last_filter_string = "" filter_and_pager_sizer.Add( self.search_filter, 5, wx.ALIGN_LEFT | wx.EXPAND \ | wx.RIGHT, border = 6) ## The pager pulldown pager_label = fpwx.label(self, _(u"Page:")) self.pager_combo = wx.ComboBox(self, -1, value="1", choices=["busy"], style=wx.CB_DROPDOWN | wx.TE_PROCESS_ENTER) self.pager_combo.Bind(wx.EVT_COMBOBOX, self.onPagechoiceClick) self.pager_combo.Bind(wx.EVT_TEXT_ENTER, self.onPagerChoiceTextEnter) filter_and_pager_sizer.Add(pager_label, 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, border=6) filter_and_pager_sizer.Add(self.pager_combo, 1) #, wx.ALIGN_RIGHT ) ## The SCROLLED FONT VIEW panel: self.scrolledFontView = ScrolledFontView(self) ## Sizer: for buttons prev, main, next bottom_buttons_sizer = wx.BoxSizer(wx.HORIZONTAL) #July 2016 #========= # The stock icon on the button was not showing under gtk3. # This stock button has not been translated into Afrikaans yet. (Dec 2007) # I can't tell you how this fkuced me around! # July 2016 - remarked : self.next_button = wx.Button(self, wx.ID_FORWARD) # I have switched to a bitmapbutton. I hope this works... ### # For future: # example = wx.BitmapButton(self, -1, fpwx.wxbmp( "xxx" ), # style = wx.NO_BORDER) # Previous button #self.previous_button = wx.BitmapButton( self, wx.ID_BACKWARD, # wx.ArtProvider.GetBitmap( # wx.ART_GO_BACK, wx.ART_BUTTON, (32,32) )) self.previous_button = wx.BitmapButton(self, wx.ID_BACKWARD, fpwx.wxbmp('icon_prev_page')) # Main button self.button_main = wx.Button(self, label=" ") self.buttMainLastLabel = " " # Next button #self.next_button = wx.BitmapButton( self, wx.ID_FORWARD, # wx.ArtProvider.GetBitmap( # wx.ART_GO_FORWARD, wx.ART_BUTTON, (32,32) )) self.next_button = wx.BitmapButton(self, wx.ID_FORWARD, fpwx.wxbmp('icon_next_page')) self.previous_button.Enable(False) # Starts out disabled bottom_buttons_sizer.Add(self.previous_button, 0, wx.EXPAND | wx.RIGHT, border=8) bottom_buttons_sizer.Add(self.button_main, 1, wx.EXPAND | wx.RIGHT, border=8) bottom_buttons_sizer.Add(self.next_button, 0, wx.EXPAND) ## Start at the top: the icon and label main_view_sizer.Add(icon_and_text_sizer, 0, wx.EXPAND) ## Sub label main_view_sizer.Add(self.status_text, 0, wx.EXPAND | wx.TOP, border=4) ## Fill the Choice and Filter main_view_sizer.Add(filter_and_pager_sizer, 0, wx.EXPAND | wx.TOP, border=8) ## Fill the SIZER FOR THE SCROLLED FONT VIEW main_view_sizer.Add(self.scrolledFontView, 1, wx.EXPAND | wx.TOP, border=8) ## Fill the bottom buttons main_view_sizer.Add(bottom_buttons_sizer, 0, wx.EXPAND | wx.TOP, border=10) ## Do the voodoo thang self.SetSizer(main_view_sizer) ## This Fit is NB. It sets the width ## for all panels to come. ## Remove it and the various AutoWrapStaticText ## objects fail hard. self.Fit() ## Bind events self.previous_button.Bind(wx.EVT_BUTTON, self.navClick) self.next_button.Bind(wx.EVT_BUTTON, self.navClick) self.Bind(wx.EVT_BUTTON, self.onMainClick, self.button_main) #.GetId() ) ## Advertise some local functions: ps.sub(left_or_right_key_pressed, self.OnLeftOrRightKey) ##DND: class FontViewPanel ps.sub(toggle_main_button, self.ToggleMainButton) ##DND: Called in gui_Fitmap ps.sub(update_font_view, self.MainFontViewUpdate) ##DND: class FontViewPanel ps.sub(reset_to_page_one, self.ResetToPageOne) ##DND: class FontViewPanel # Tried to implement wrapping on some of the labels. # It flickers and fails. F**k it. #def on_evt_size(self,e): #w = e.GetSize()[0] #w = max(w+5,200) #self.status_text.Wrap(w) # e.Skip() def on_clear_button_click(self): #, event ): self.filter = "" # Clear the BIR toggle buttons self.setAllBIRFalse() ## Now command a change of the view. ## First, return user to page 1: self.pageindex = 1 self.filterAndPageThenCallCreateFitmaps() # A GTK bug demands this move. Restore the ESC key func. self.button_main.SetFocus() def setOneBIR(self, idy, truth): self.BIR[idy]['truth'] = truth self.BIR[idy]['instance'].SetValue(truth) def setAllBIRFalse(self): for idy in self.BIR.keys(): self.setOneBIR(idy, False) def onBIR(self, e): idy = e.GetId() toggstate = self.BIR[idy]['instance'].GetValue() self.BIR[idy]['truth'] = toggstate ss = "" if self.BIR[idy]['style'] == "regular": # only if this is toggle on, do we want # action anything: if toggstate is True: # can't have regular with bold/italic self.setAllBIRFalse() # switch all off self.setOneBIR(idy, True) ss = "regular|normal" else: self.setOneBIR(self.idRegular, False) for idy, dic in self.BIR.iteritems(): # Builds an AND regex (a space is "AND") if dic['truth']: ss += "{}{}".format(dic['style'], " ") ss = ss[:-1] # Go alter the search text box self.search_filter.set_BIR(ss) # Start the process self.startSearch(ss) def do_search(self, sstring): """ SearchFilter control will call this with sstring being the filter term. """ self.startSearch(sstring) self.button_main.SetFocus() return True # to store in history def startSearch(self, terms): self.filter = terms ## First, return user to page 1: self.pageindex = 1 ## Now command a change of the view. self.filterAndPageThenCallCreateFitmaps() def filterAndPageThenCallCreateFitmaps(self): """ Figure out what list of fonts to draw, divide them into pages, then go make Fitmaps out of them. """ self.total_number_of_pages = 1 # A default ## Is there anything there to view? if len(fpsys.state.viewobject) > 0: ## JUNE 2009 : Changes made ## If the filter string changed from last time, signal so. filter_changed = False if self.filter != self.last_filter_string: filter_changed = True self.last_filter_string = self.filter ## If the filter did change OR we have a blank ## filteredViewObject, then make a new one. if not fpsys.state.filteredViewObject or filter_changed: fpsys.state.filteredViewObject = fontyfilter.doFilter( self.filter) # Uses the external module to filter. ## STEP 2 : Figure out how many pages we have to display current_page = self.pageindex - 1 num_in_one_page = fpsys.config.numinpage total_num_fonts = len(fpsys.state.filteredViewObject) ## Many thanks to Michael Hoeft for this fix! I suck at math :) # I miss the right words to explain this step, therefore an example: # 23 / 10 = 2 # 23 % 10 = 3 > modulo > bool(3) = True = 1 # ----------------------------------------- # 2 + 1 = 3 > 3 pages # # 40 / 10 = 4 # 40 % 10 = 0 > modulo > bool(0) = False = 0 # ------------------------------------------ # 4 + 0 = 4 > 4 pages self.total_number_of_pages = (total_num_fonts / num_in_one_page) \ + bool(total_num_fonts % num_in_one_page) #leaf thru the pages to the one we are on now. start = current_page * num_in_one_page fin = start + num_in_one_page # Make sure we don't overshoot. if fin > len(fpsys.state.filteredViewObject): fin = len(fpsys.state.filteredViewObject) ## Extract a single page of fonts to display sublist = fpsys.state.filteredViewObject[start:fin] ## Empty the choice control. self.pager_combo.Clear() ## Now refill it [ self.pager_combo.Append(str(n)) for n in xrange(1, self.total_number_of_pages + 1) ] self.pager_combo.SetSelection(self.pageindex - 1) #self.choiceSlider.SetRange(1,self.total_number_of_pages+1) ## The viewobject is empty anyway. else: sublist = [] if self.total_number_of_pages == 1: self.pager_combo.Enable(False) else: self.pager_combo.Enable(True) # Tell my child to draw the fonts self.scrolledFontView.MinimalCreateFitmaps(sublist) self.EnableDisablePrevNext() def _setup_state_dicts(self): """ These dicts are used in MainFontViewUpdate. They hold the variables and such for various states of the app i.t.o Source/View and Target. """ # Some common strings nadatd = _("There's nothing much to do.") ntd = _("Choose some fonts") # The "nothing add remove samepogs" dict # Will be used in the main dict below. n_a_r_s = { 'n': { #Nothing 'btext': ntd, 'action': "NOTHING_TO_DO", 'info': nadatd, 'cantick': False, 'tmap': self._TICK #default, but is not drawn. }, 's': { #Same 'btext': ntd, 'action': "NOTHING_TO_DO", 'info': nadatd, 'cantick': False, 'tmap': self._TICK }, '-': { #Remove 'btext': _("Remove fonts from {VIEW}"), 'info': _("You can remove fonts from this source Pog."), 'action': "REMOVE", 'cantick': True, 'tmap': self._CROSS }, '+': { #Append 'btext': _("Put fonts into {TARGET}"), 'info': _("You can append fonts to the active target Pog \"{TARGET}\"." ), 'action': "APPEND", 'cantick': True, 'tmap': self._TICK } } # A way to test for recurse flag later recurse_test = lambda: _(" (and all sub-folders.)") \ if fpsys.config.recurseFolders else "" # Some common strings vF = _("Viewing source Folder \"{VIEW}\"{{RT}}") vP = _("Viewing source Pog \"{VIEW}\"") choose_source = _("Choose a Source Pog or Folder.") nochangetarget = _("The target Pog \"{TARGET}\" is installed. "\ "It can't be changed.") # The main "label" dict. # See remarks in MainFontViewUpdate for details. self.lbl_d = { ## Empty to Nothing 'EN' : { 'lab': _("There are no fonts in here."), 'tip': choose_source, 'nars': n_a_r_s['n'] }, ## Empty to Pog 'EP' : { 'lab': _("Source is empty. The active target Pog is \"{TARGET}\""), 'tip': choose_source, 'nars': n_a_r_s['n'] }, ## Folder to Nothing 'FN' : { 'lab': vF, 'nars': n_a_r_s['n'], 'rtest': recurse_test }, ## Pog to Nothing 'PrN': { 'lab': _("Viewing (installed Pog) \"{VIEW}\""), 'tip': _("You can't change an installed Pog."), 'nars': n_a_r_s['n'] }, 'PwN': { # '-' Remove from source pog 'lab': _("Viewing (editable Pog) \"{VIEW}\""), 'tip': _("There is no active target."), 'nars': n_a_r_s['-'] }, ## Folder to Pog 'FPr': { 'lab': vF, 'tip': nochangetarget, 'nars': n_a_r_s['n'], 'rtest': recurse_test, }, 'FPw': { # Add to target Pog 'lab': vF, 'nars': n_a_r_s['+'], 'rtest': recurse_test, }, ## Pog to Pog 'PPr': { 'lab': vP, 'tip': nochangetarget, 'nars': n_a_r_s['n'], }, 'PPs': { 'lab': _("Source and Target \"{VIEW}\" are the same."), 'tip': _("Clear the target, or choose another Pog."), 'nars': n_a_r_s['s'], }, 'PPw': { # Add to target Pog 'lab': vP, 'nars': n_a_r_s['+'], } } def MainFontViewUpdate(self): """ Vital routine - the heart if the app. This decides what to do based on what has been selected. It draws the controls and the fonts as appropriate. It also sets flags in fpsys.state """ ## If any DismissablePanels are open, hide them: ps.pub(ensure_fontview_shown) ## Get shorter vars to use. V = fpsys.state.viewobject T = fpsys.state.targetobject VC = fpsys.state.viewpattern # View Char TC = fpsys.state.targetpattern # Target Char P = VC + TC # a two-char flag ## Okay, let's make a key:.. ## [S][rw][T][rws] <-- key's basic shape ## Where S is Source, T is Target: ## E == Empty View/Source - no fonts in chosen Source. ## N == Empty Target - no fonts. ## P is Pog ## F is Folder ## And rw: ## r is read (i.e. only can view) ## w is write (i.e. remove or append) ## s is same (S/T pogs are the same) ## Example: ## PrN -> Source pog+read. No target pog. ## i.e: We are viewing a pog that is installed ## (no write.) ## FPw -> Source folder. Target pog+write ## i.e. We are viewing a folder and have a Pog ## which is *not* installed, so can be 'written' ## i.e. have fonts appended. # EN -> EN (Empty source, No target) # EP -> EP (Empty source, Pog target (weird) ) # FN -> FN (Folder source, No target) if P in ('EN', 'EP', 'FN'): key = P # PN -> PrN or PwN # Pog source, No target. # Because 1) source is a Pog # and 2) there's no target, # the Pog can be: # r = view it only (i.e. it's installed) # w = remove fonts elif P == "PN": key = "PrN" if V.isInstalled() else "PwN" # FP -> FPr or FPw # Folder source, Pog target. Target r or w. # Similar logic. If target pog is installed # it can't be added-to, i.e. it's 'r' only. # else it can be 'w' elif P == "FP": key = "FPr" if T.isInstalled() else "FPw" # PP -> PPr or PPw or PPs # Target pog is either 'r' or 'w' or ... # Target pog 's' means source and target are same. elif P == "PP": if fpsys.state.samepogs: key = "PPs" else: key = "PPr" if T.isInstalled() else "PPw" # Some kind of error else: print _("FontView state error: Pattern is \"{}\"").format(P) raise SystemExit ## ..and use it to fetch from dict self.lbl_d d = self.lbl_d[key] # Little func to replace substrings. def rep(s): if "VIEW" in s: s = s.format(VIEW=V.label()) if "TARGET" in s: s = s.format(TARGET=T.label()) return s lab = rep(d['lab']) ## Do we add extra text about recursing? rtest = d.get('rtest', None) if rtest: lab = lab.format(RT=rtest()) # yes ## using dict n_a_r_s nars = d['nars'] fpsys.state.cantick = nars['cantick'] fpsys.state.action = nars['action'] self.TICKMAP = nars['tmap'] ## Enable/Disable the Purge menu item ## Switch it off, then: if the view is a Pog ## and it's *not* installed, we can switch it on. ## (Because you can't purge an installed font.) ## This has nothing to do with whatever target ## may be selected. ps.pub(toggle_purge_menu_item, False) if VC == "P" and not V.isInstalled(): ps.pub(toggle_purge_menu_item, True) self.buttMainLastLabel = rep(nars['btext']) self.main_font_info_label.SetLabel(lab) self.main_font_info_label.Show() i = nars['info'] t = d.get('tip', "") st = rep(u"{} {}".format(i, t)) self.status_text.SetLabel(st) self.ToggleMainButton() fpsys.markInactive() self.filterAndPageThenCallCreateFitmaps() def onMainClick(self, evt): """ Removes fonts, or Appends fonts. Depends on situation in fpsys.state """ #Saved by Robin Dunn, once again ! ! ! xPos, yPos = self.scrolledFontView.GetViewStart() wx.BeginBusyCursor() doupdate = False ## Let's determine what kind of thing to do: if fpsys.state.action == "REMOVE": ## We have a pog in viewobject and we must remove the ## selected fonts from it. vo = fpsys.state.viewobject victims = [] dowrite = False for fi in vo: if fi.ticked: victims.append(fi) #Put it into another list dowrite = True for fi in victims: vo.remove(fi) #Now remove it from the vo del victims if dowrite: fpsys.flushTicks() bug = False try: vo.write() except (fontybugs.PogWriteError), e: bug = True ps.pub(show_error, unicode(e)) doupdate = True if not bug: ps.pub(print_to_status_bar, _("Selected fonts have been removed.")) else: ps.pub(print_to_status_bar,_( "There was an error writing the pog "\ "to disk. Nothing has been done.")) ## APPEND - Copy font to a pog. if fpsys.state.action == "APPEND": ## We must append the fonts to the Pog vo = fpsys.state.viewobject to = fpsys.state.targetobject # pointless print: #print _("Copying fonts from {source} to {target}").format(**{ # "source":vo.label(), "target":to.label()}) dowrite = False for fi in vo: if fi.ticked: to.append(fi) dowrite = True if dowrite: #Ensure we have no more ticks after a succ. xfer. fpsys.flushTicks() bug = False try: to.write() except (fontybugs.PogWriteError), e: bug = True ps.pub(show_error, unicode(e)) doupdate = True if not bug: ps.pub( print_to_status_bar, _("Selected fonts are now in \"{}\".").format( to.label())) else: ps.pub(print_to_status_bar,_( "There was an error writing the pog to disk. " \ "Nothing has been done"))
class NoteBook(wx.Notebook): """ THIS IS THE VIEW or SOURCE of fonts. Has two tabs - Folders and Pogs """ def __init__(self, parent, name="notebook_not_named"): wx.Notebook.__init__(self, parent, style=wx.NB_TOP, name = name) self.imlist = wx.ImageList(16, 16) pan1 = wx.Panel(self) ## THE DIR CONTROL self.dircontrol = DirControl(pan1) # Get a ref to the dircontrol. self.tree = self.dircontrol.GetTreeCtrl() ## Add them to a sizer box = wx.BoxSizer(wx.VERTICAL) box.Add( self.dircontrol,1, wx.EXPAND ) pan1.SetSizer(box) box.Layout() self.pogindexselected = 0 ## The SOURCE POG control pan2 = wx.Panel(self) page = 0 s = None if fpsys.state.viewpattern == "P": s = fpsys.state.viewobject.name if s == "EMPTY": s= None #Very first run, the view will be an EMPTY object. page = 1 self.ctrlPogSource = PogChooser(pan2, whoami="SOURCEPOG", select = s) ps.sub( source_pog_has_been_selected, self.OnViewPogClick) ##DND: class NoteBook ps.sub( select_no_view_pog, self.SelectNoView) ##DND: class NoteBook ps.sub( add_pog_item_to_source, self.AddItem ) #DND: class NoteBook ps.sub( remove_pog_item_from_source, self.RemoveItem ) #DND: class NoteBook ## Dud tree events, causing bad behaviour: ## EVT_LIST_ITEM_SELECTED ## EVT_LEFT_UP ## Bind to another event solve the problem of ## EVT_LEFT_UP firing when the little ## open-branch/tree arrow was pressed. ## 5.3.2009 Michael Hoeft ## Nov 2017: Also subscribe this for use in wxgui - since ## I moved the recurseFolders into there. ps.sub( fake_click_the_source_dir_control, self.__onDirCtrlClick ) self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.__onDirCtrlClick) ## Had a context menu, but not using it. #self.tree.Bind(wx.EVT_CONTEXT_MENU, self.OnContextMenu) box2 = wx.BoxSizer(wx.HORIZONTAL) box2.Add(self.ctrlPogSource,1,wx.EXPAND) pan2.SetSizer(box2) box2.Layout() self.AddPage(pan1, _("Source Folders")) self.AddPage(pan2, _("Source Pogs")) # sadly, the artprovider icons suck, and I can't get access # to the "stock items" either. F**k it. source_folder_icon = self.imlist.Add( fpwx.wxbmp('icon_source_folder_16x16') ) source_pog_icon = self.imlist.Add( fpwx.wxbmp('icon_source_pog_16x16') ) self.AssignImageList(self.imlist) self.SetPageImage(0, source_folder_icon) self.SetPageImage(1, source_pog_icon) self.SetSelection(page) # Dec 2017: Remarked this Bind. See notes in old handler #self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, # self.onPageChanged) # Bind page changed event ## If the app is started with a Folder as the Source, then ## check if we must recurse. If so, fake a click to kick that off. ## Sept 2017 if fpsys.state.viewpattern == "F": self.__onDirCtrlClick() # Fake an event def onPageChanged(self, e): """ The notebook page change handler. Remarked the Bind Dec 2017. A click on the notebook tabs should not make assumptions about what source to select. By ignoring this, the new eye icon remains on the last selected directory. It's not perfect, because coming back to the dir control shows what was selected before, but you can't click on the same selection and get a result. You must click-off and click-on to get the view to update. I moved the UnselectAll call into OnViewPogClick so that when you click on an actual source Pog, the tree control now moves out of play. I leave this code for the future. """ self.ctrlPogSource.ClearLastIndex() if self.GetSelection() == 0: # The dircontrol #pass ## I want to force the dir control to clear the selection. ## Reason: When you return to this control (from Pog page), ## the selection from last visit is still there. ## Clicking on it again does NOT UPDATE the font view. ## This is wierd. So, clearing the selection makes it moot. ## This actually works: # wx.CallAfter( self.__onDirCtrlClick) # However.. I think it's weird. # When you click a tab in the notebook, it should # not make assumptions about what you mean. self.tree.UnselectAll() # Found this method in the wxpython book. def __onDirCtrlClick(self, evt = None): if evt: # I want this event to eventually arrive in # the Atree class; thus Skip() # (See gui_DirChooser.py) evt.Skip() # Happens when this handler finishes. wx.BeginBusyCursor() #Thanks to Suzuki Alex on the wxpython list! p = self.dircontrol.GetPath() try: fpsys.instantiateViewFolder( p, fpsys.config.recurseFolders ) fpsys.config.lastdir = p except fontybugs.FolderHasNoFonts, err: pass # update_font_view handles this with a std message. ps.pub(reset_to_page_one)# reset before updating! ps.pub(update_font_view) wx.EndBusyCursor() wx.CallAfter( self.SetFocus )
def onMiddleClick(self, event): ps.pub( open_settings_panel)