44
55import curses
66
7+ import stagger
78import npyscreen as npy
89
10+ from . import _const
11+
912class ClidActionController (npy .ActionControllerSimple ):
1013 """Base class for the command line at the bottom of the screen"""
1114
@@ -30,10 +33,10 @@ def set_up_handlers(self):
3033 self .handlers [curses .KEY_END ] = self .h_end
3134 self .handlers [curses .KEY_HOME ] = self .h_home
3235
33- def h_home (self , input ):
36+ def h_home (self , char ):
3437 self .cursor_position = 0
3538
36- def h_end (self , input ):
39+ def h_end (self , char ):
3740 self .cursor_position = len (self .value )
3841
3942
@@ -75,30 +78,30 @@ def set_up_handlers(self):
7578 self .vim_add_handlers ()
7679 self .handlers [curses .ascii .ESC ] = self .h_vim_normal_mode # is a bit slow
7780
78- def h_addch (self , input ):
81+ def h_addch (self , char ):
7982 if self .parent .in_insert_mode : # add characters only if in insert mode
80- super ().h_addch (input )
83+ super ().h_addch (char )
8184
82- def h_vim_insert_mode (self , input ):
85+ def h_vim_insert_mode (self , char ):
8386 """Enter insert mode"""
8487 self .parent .in_insert_mode = True
8588 self .vim_remove_handlers () # else `k`, j`, etc will not be added to text(will still act as keybindings)
8689
87- def h_vim_normal_mode (self , input ):
90+ def h_vim_normal_mode (self , char ):
8891 """Exit insert mode by pressing Esc"""
8992 self .parent .in_insert_mode = False
9093 self .cursor_position -= 1 # just like in vim
9194 self .vim_add_handlers () # removed earlier when going to insert mode
9295
93- def h_vim_append_char (self , input ):
96+ def h_vim_append_char (self , char ):
9497 """Append characters, like `a` in vim"""
95- self .h_vim_insert_mode (input )
98+ self .h_vim_insert_mode (char )
9699 self .cursor_position += 1
97100
98- def h_vim_append_char_at_end (self , input ):
101+ def h_vim_append_char_at_end (self , char ):
99102 """Add characters to the end of the line, like `A` in vim"""
100- self .h_vim_insert_mode (input )
101- self .h_end (input ) # go to the end
103+ self .h_vim_insert_mode (char )
104+ self .h_end (char ) # go to the end
102105
103106class ClidVimTitleText (npy .TitleText ):
104107 _entry_type = ClidVimTextfield
@@ -122,3 +125,122 @@ class ClidCommandLine(npy.fmFormMuttActive.TextCommandBoxTraditional, ClidTextfi
122125 # # self.color = 'DEFAULT'
123126 # # self.show_bold = False
124127 pass
128+
129+
130+ class ClidEditMeta (npy .ActionFormV2 ):
131+ """Edit the metadata of a track.
132+
133+ Attributes:
134+ files(list): List of files whose tags are being edited.
135+ _label_textbox(ClidTextfield):
136+ Text box which acts like a label(cannot be edited).
137+ _title_textbox(ClidTextfield):
138+ Text box with a title, to be used as input field for tags.
139+ in_insert_mode(bool):
140+ Used to decide whether the form is in insert/normal
141+ mode(if vi_keybindings are enabled). This is actually
142+ set as an attribute of the parent form so that all
143+ text boxes in the form are in the same mode.
144+ """
145+ def __init__ (self , * args , ** kwags ):
146+ super ().__init__ (* args , ** kwags )
147+ self .handlers .update ({
148+ '^S' : self .h_ok ,
149+ '^Q' : self .h_cancel
150+ })
151+ self .in_insert_mode = False
152+ self .files = self .parentApp .current_file
153+
154+ def set_textbox (self ):
155+ """Set the text boxes to be used(with or without vim-bindings).
156+ Called by child classes.
157+ """
158+ if self .parentApp .settings ['vim_mode' ] == 'true' :
159+ self ._title_textbox = ClidVimTitleText # vim keybindings if enabled
160+ self ._label_textbox = ClidVimTextfield
161+ else :
162+ self ._title_textbox = ClidTitleText
163+ self ._label_textbox = ClidTextfield
164+
165+ def create (self ):
166+ self .set_textbox ()
167+ self .tit = self .add (self ._title_textbox , name = 'Title' )
168+ self .nextrely += 1
169+ self .alb = self .add (self ._title_textbox , name = 'Album' )
170+ self .nextrely += 1
171+ self .art = self .add (self ._title_textbox , name = 'Artist' )
172+ self .nextrely += 1
173+ self .ala = self .add (self ._title_textbox , name = 'Album Artist' )
174+ self .nextrely += 2
175+ self .gen = self .add (self ._title_textbox , name = 'Genre' )
176+ self .nextrely += 1
177+ self .dat = self .add (self ._title_textbox , name = 'Date/Year' )
178+ self .nextrely += 1
179+ self .tno = self .add (self ._title_textbox , name = 'Track Number' )
180+ self .nextrely += 2
181+ self .com = self .add (self ._title_textbox , name = 'Comment' )
182+
183+ def resolve_genre (self , num_gen ):
184+ """Convert numerical genre values to readable values. Genre may be
185+ saved as a str of the format '(int)' by applications like EasyTag.
186+
187+ Args:
188+ num_gen (str): str representing the genre.
189+
190+ Returns:
191+ str: Name of the genre (Electronic, Blues, etc). Returns
192+ num_gen itself if it doesn't match the format.
193+ """
194+ match = _const .GENRE_PAT .findall (num_gen )
195+
196+ if match :
197+ try :
198+ return _const .GENRES [int (match [0 ])]
199+ except IndexError :
200+ return ''
201+ else :
202+ return num_gen
203+
204+ def h_ok (self , char ):
205+ """Handler to save the tags"""
206+ self .on_ok ()
207+
208+ def h_cancel (self , char ):
209+ """Handler to cancel the operation"""
210+ self .on_cancel ()
211+
212+ def on_cancel (self ): # char is for handlers
213+ """Switch to standard view at once without saving"""
214+ self .editing = False
215+ self .parentApp .switchForm ("MAIN" )
216+
217+ def on_ok (self ): # char is for handlers
218+ """Save and switch to standard view"""
219+ # date format check
220+ m = _const .DATE_PATTERN .match (self .dat .value )
221+ if m is None or m .end () != len (self .dat .value ):
222+ npy .notify_confirm (message = 'Date should be of the form YYYY-MM-DD HH:MM:SS' ,
223+ title = 'Invalid Date Format' , editw = 1 )
224+ return None
225+ # track number check
226+ track = self .tno .value or '0' # automatically converted to int by stagger
227+ if not track .isnumeric ():
228+ npy .notify_confirm (message = 'Track number can only take integer values' ,
229+ title = 'Invalid Track Number' , editw = 1 )
230+ return None
231+ # FIXME: values of tags are reset to initial when ok is pressed(no prob with ^S)
232+
233+ for mp3 in self .files :
234+ meta = stagger .read_tag (mp3 )
235+ for tbox , field in _const .TAG_FIELDS :
236+ # equivalent to `meta.title = self.tit.value`...
237+ tag = getattr (self , tbox ).value # get value to be written to file
238+ setattr (meta , field , tag )
239+ meta .write ()
240+
241+ main_form = self .parentApp .getForm ("MAIN" )
242+ # show the new tags in the status line
243+ main_form .wMain .set_status (filename = main_form .wMain .get_selected (), force = True )
244+
245+ self .editing = False
246+ self .parentApp .switchForm ("MAIN" )
0 commit comments