diff --git a/Main.py b/Main.py index 604ebae..0063a9c 100644 --- a/Main.py +++ b/Main.py @@ -8,10 +8,21 @@ from os.path import exists from os import listdir from tkcalendar import DateEntry +from PIL import Image, ImageTk +from functools import cmp_to_key # safeguard for the treeview automated string conversion problem PREFIX = '<@!PREFIX>' +def set_icon(object): + """ + This function sets the icon of a tkinter window (passed in as `object`) to + the CFM icon. + """ + im = Image.open('assets/CFM.ico') + photo = ImageTk.PhotoImage(im) + object.wm_iconphoto(True, photo) + # change to desired resolution def set_resolution(window, width, height): @@ -138,6 +149,42 @@ def __init__(self, parent, controller): self.treeview.bind('', lambda event: self.deselect()) self.treeview.bind('', lambda event: self.show_statistics()) + # *Ordered* list of columns (so we can display them in a fixed order) + self.columns = [ + 'name', + 'pep', + 'type', + 'msg', + 'call', + 'photos', + 'gifs', + 'videos', + 'files' + ] + self.column_titles = columns + + # Ordered list of columns to sort by + self.sort_columns = [] + + # Store the biases for each column in one place + self.column_biases = { + "name": "stringwise", + "pep": "numberwise", + "type": "stringwise", + "msg": "numberwise", + "call": "numberwise", + "photos": "numberwise", + "gifs": "numberwise", + "videos": "numberwise", + "files": "numberwise", + } + + """ + Store whether each column is reversed. We only need this if we are doing + a multi-sort. + """ + self.columns_reversed = dict() + # show frame title ttk.Label( self.main, text=f'{self.module.TITLE_NUMBER_OF_MSGS}: ', foreground='#ffffff', background='#232323', @@ -163,6 +210,21 @@ def __init__(self, parent, controller): command=self.search ).pack(side='top', pady=10) + + # Multi-sort button opens the sort-editor UI + ttk.Button( + self.nav, text=self.module.TITLE_MULTI_SORT, padding=5, + command = lambda : + MultiSortPopup( + self.controller, + self.columns[:], # Pass a copy + {**self.column_titles}, # Pass a copy + self.columns_reversed, + self.sort_columns, + lambda : self.apply_multi_sort() + ) + ).pack(side='top', pady=10) + # show exit button ttk.Button( self.nav, image=self.controller.ICON_EXIT, text=self.module.TITLE_EXIT, compound='left', padding=5, @@ -212,16 +274,27 @@ def upload_data(self): conversations = len(listdir(self.controller.get_directory())) LoadingPopup(self.controller, conversations, self.treeview) - # enable column sorting on treeview - self.treeview.heading('msg', command=lambda col='msg': self.sort_treeview(col, False, 'numberwise')) - self.treeview.heading('name', command=lambda col='name': self.sort_treeview(col, False, 'stringwise')) - self.treeview.heading('type', command=lambda col='type': self.sort_treeview(col, False, 'stringwise')) - self.treeview.heading('call', command=lambda col='call': self.sort_treeview(col, False, 'numberwise')) - self.treeview.heading('photos', command=lambda col='photos': self.sort_treeview(col, False, 'numberwise')) + # bind general purpose handler to column clicks + self.treeview.heading('msg', command=lambda col='msg': self.click_column(col, False, 'numberwise')) + self.treeview.heading('name', command=lambda col='name': self.click_column(col, False, 'stringwise')) + self.treeview.heading('type', command=lambda col='type': self.click_column(col, False, 'stringwise')) + self.treeview.heading('call', command=lambda col='call': self.click_column(col, False, 'numberwise')) + self.treeview.heading('photos', command=lambda col='photos': self.click_column(col, False, 'numberwise')) + except FileNotFoundError: print('>MainPage/upload_data THROWS FileNotFoundError, NOTIFY OP IF UNEXPECTED') - # invoked by pressing the column headers + + def click_column(self, col, order, bias): + """ + Clicking a column (and therefore doing a sort on it) discards multi-sort + info + """ + self.sort_columns.clear() + self.columns_reversed.clear() + + self.sort_treeview(col, order, bias) + def sort_treeview(self, column, order, bias): # Cache the get_children call children = self.treeview.get_children('') @@ -241,6 +314,72 @@ def sort_treeview(self, column, order, bias): # Reverse the order for the next sort self.treeview.heading(column, command=lambda: self.sort_treeview(column, not order, bias)) + def apply_multi_sort(self): + """ + This function sorts the rows based on the ordering stored in + `self.sort_columns`. + + To achieve this, the `compare` function tries to break ties based + each successive column in the ordering, before giving up and declaring + a tie. + """ + def compare(a, b, ordering): + # a and b are *rows* of data (dictionaries) + if ordering == []: + # We have nothing left to break ties on + return 0 + + # We will try to break the tie on this column + column_name = ordering[0] + bias = self.column_biases[column_name] + reverse_multiplier = -1 if self.columns_reversed[column_name] else 1 + + # Retrieve the appropriate column from each row + a_value = a[column_name] + b_value = b[column_name] + + if bias == "stringwise": + if a_value < b_value: return -1 * reverse_multiplier + elif a_value > b_value: return 1 * reverse_multiplier + elif bias == "numberwise": + a_value, b_value = int(a_value), int(b_value) + if a_value < b_value: return -1 * reverse_multiplier + elif a_value > b_value: return 1 * reverse_multiplier + else: + raise Exception(f"Undefined bias '{bias}'") + + """ + If we made it here, then the values are equal when compared + on the current column value. + + We continue with the next column in the ordering. + """ + return compare(a, b, ordering[1:]) + + def compare_wrapper(a, b): + (_, a) = a + (_, b) = b + + return compare(a, b, self.sort_columns) + + # Retrieve all of the rows of the dataset + children = self.treeview.get_children('') + rows = [ + (k, + { + column_name: self.treeview.set(k, column_name) + for column_name in self.column_biases + } + ) + for k in children + ] + + rows.sort(key = cmp_to_key(compare_wrapper)) + + for (idx, (k, _)) in enumerate(rows): + self.treeview.move(k, '', idx) + + # invoked on double left click on any treeview listing def show_statistics(self): try: @@ -287,7 +426,7 @@ def __init__(self, *args, **kwargs): # global window customization self.title('Counter for Messenger') - self.iconbitmap('assets/CFM.ico') + set_icon(self) # frame container setup self.container = tk.Frame(self) @@ -447,7 +586,7 @@ def __init__(self, controller): # profile window customization self.title(self.module.TITLE_PROFILE) - self.iconbitmap('assets/CFM.ico') + set_icon(self) self.focus_set() self.grab_set() @@ -501,7 +640,7 @@ def __init__(self, controller): # settings window customization self.title(self.module.TITLE_SETTINGS) - self.iconbitmap('assets/CFM.ico') + set_icon(self) self.focus_set() self.grab_set() @@ -607,7 +746,7 @@ def __init__(self, controller, chat_total, treeview): # loading window customization self.title(f'{self.module.TITLE_LOADING}...') - self.iconbitmap('assets/CFM.ico') + set_icon(self) self.resizable(False, False) self.focus_set() self.grab_set() @@ -678,7 +817,7 @@ def __init__(self, controller, selection): # statistics window customization self.title(self.module.TITLE_STATISTICS) - self.iconbitmap('assets/CFM.ico') + set_icon(self) self.focus_set() self.grab_set() @@ -738,6 +877,359 @@ def __init__(self, controller, selection): listbox.insert('end', f'{self.module.TITLE_PER_MONTH} - {all_msgs / (sec_since_start / (30 * 86400)):.2f}') listbox.insert('end', f'{self.module.TITLE_PER_YEAR} - {all_msgs / (sec_since_start / (365 * 86400)):.2f}') +class MultiSortPopup(tk.Toplevel): + """ + This class implements the sort-editor popup + """ + def __init__(self, controller, columns, column_titles, columns_reversed, sort_columns, apply_callback): + tk.Toplevel.__init__(self) + self.controller = controller + self.module = self.controller.lang_mdl + + # This popup is bigger to make room for all the additional buttons + set_resolution(self, 800, 600) + + self.columns = columns + self.column_titles = column_titles + + """ + Bind sort info so we can mutate it later - it's important we don't break + these aliases + """ + self.sort_columns = sort_columns + self.columns_reversed = columns_reversed + + # We can call this function to apply the sort + self.apply_callback = apply_callback + + """ + Temporary ordering - we will keep this in sync with a listbox. + + We initialize the ordering with the ordering from the MainPage, if one + exists. + """ + self.temp_ordering = self.sort_columns[:] + self.temp_reversed = {**self.columns_reversed} + + # Window customization + self.title(self.module.TITLE_CONFIGURE_MULTI_SORT) + set_icon(self) + self.focus_set() + self.grab_set() + + # Show header + ttk.Label( + self, text=self.module.TITLE_MULTI_SORT, font=('Ariel', 24) + ).pack(side='top', pady=20) + + """ + Listbox Frame + """ + listbox_frame = tk.Frame(self) + listbox_frame.pack(fill=tk.X, padx=50) + + # Have columns fill width + listbox_frame.grid_columnconfigure(0, weight=1) + listbox_frame.grid_columnconfigure(1, weight=1) + + # Listbox of "available" columns + tk.Label( + listbox_frame, + text=self.module.TITLE_AVAILABLE_COLUMNS + ).grid(row=0, column=0) + self.available_listbox = tk.Listbox(listbox_frame) + self.available_listbox.grid(row=1, column=0, sticky='nesw') + + # Fill the listbox with all the columns + for column_name in self.columns: + column_title = self.column_titles[column_name] + self.available_listbox.insert(tk.END, column_title) + + # Listbox to configure sort_order (empty to begin with) + tk.Label( + listbox_frame, + text=self.module.TITLE_SORT_ORDER + ).grid(row=0, column=1) + self.sort_order_listbox = tk.Listbox(listbox_frame) + self.sort_order_listbox.grid(row=1, column=1, columnspan=1, sticky='nesw') + + # Fill the listbox with restored columns + for column_name in self.temp_ordering: + text = self.get_entry_text(column_name) + self.sort_order_listbox.insert(tk.END, text) + + # "Add" and "Remove" buttons + tk.Button( + listbox_frame, + text=self.module.TITLE_ADD, + command = lambda : self.add_clicked() + ).grid(row=2,column=0) + + tk.Button( + listbox_frame, + text=self.module.TITLE_REMOVE, + command = lambda : self.remove_clicked() + ).grid(row=3,column=0) + + # "Move up" and "Move down" buttons + tk.Button( + listbox_frame, + text=self.module.TITLE_MOVE_UP, + command = lambda : self.move_up_clicked() + ).grid(row=2, column=1) + + tk.Button( + listbox_frame, + text=self.module.TITLE_MOVE_DOWN, + command = lambda : self.move_down_clicked() + ).grid(row=3, column=1) + + # "Reverse" button + tk.Button( + listbox_frame, + text=self.module.TITLE_REVERSE, + command = lambda : self.reverse_clicked() + ).grid(row=4,column=0) + + # "Clear" button + tk.Button( + listbox_frame, + text=self.module.TITLE_CLEAR, + command = lambda : self.clear() + ).grid(row=4,column=1) + + # "Apply" button + tk.Button( + self, + text=self.module.TITLE_APPLY, + command = lambda : self.apply() + ).pack() + + def get_entry_text(self, column_name): + """ + Helper function that returns a string representation of `column_name` + for display in the listboxes. + """ + text = self.column_titles[column_name] + + if self.temp_reversed[column_name]: + return f"{text} (reversed)" + else: + return text + + def add_to_sort(self, column_name): + """ + Add a column to the ordering + """ + # Add to the temporary ordering + self.temp_ordering.append(column_name) + + # Not reversed by default + self.temp_reversed[column_name] = False + + # Add to listbox + text = self.get_entry_text(column_name) + self.sort_order_listbox.insert(tk.END, text) + + def remove_from_sort(self, column_name): + """ + Remove a column from the ordering + """ + # Retrieve the index + idx = self.temp_ordering.index(column_name) + + # Delete from the temporary ordering + self.temp_ordering.pop(idx) + + # Delete from the listbox + self.sort_order_listbox.delete(idx) + + # Delete reversed info + del self.temp_reversed[column_name] + + def move_up(self, column_name): + """ + Move a column up in the sort ordering + """ + # Retrieve the index + idx = self.temp_ordering.index(column_name) + new_idx = max(0, idx - 1) + + # Move in the temporary ordering + self.temp_ordering.pop(idx) + self.temp_ordering.insert(new_idx, column_name) + + # Move in the listbox + self.sort_order_listbox.delete(idx) + text = self.get_entry_text(column_name) + self.sort_order_listbox.insert(new_idx, text) + + def move_down(self, column_name): + """ + Move a column down in the sort ordering + """ + # Retrieve the index + idx = self.temp_ordering.index(column_name) + new_idx = min(len(self.temp_ordering) - 1, idx + 1) + + # Move in the temporary ordering + self.temp_ordering.pop(idx) + self.temp_ordering.insert(new_idx, column_name) + + # Move in the listbox + self.sort_order_listbox.delete(idx) + text = self.get_entry_text(column_name) + self.sort_order_listbox.insert(new_idx, text) + + def reverse(self, column_name): + """ + Specify that a column should be sorted in reverse order. + """ + # Reverse in the dictionary + self.temp_reversed[column_name] = not self.temp_reversed[column_name] + + # Retrieve the index + idx = self.temp_ordering.index(column_name) + + # Delete current entry in listbox + self.sort_order_listbox.delete(idx) + + # Rewrite entry + text = self.get_entry_text(column_name) + self.sort_order_listbox.insert(idx, text) + + def clear(self): + """ + Reset the sort ordering column + """ + # Reset state + self.temp_ordering = [] + self.temp_reversed = dict() + + # Clear listbox + self.sort_order_listbox.delete(0, tk.END) + + def apply(self): + """ + Sort according to the ordering the user builds and close this popup. + """ + # Overwrite old state + self.sort_columns.clear() + self.columns_reversed.clear() + + # Write new state + self.sort_columns.extend(self.temp_ordering) + self.columns_reversed.update(self.temp_reversed) + + # Call the callback + self.apply_callback() + + # Close the window + self.destroy() + + def add_clicked(self): + """ + "Add" button clicked + """ + idx = self.available_listbox.curselection() + if len(idx) <= 0: return + (idx,) = idx + column_name = self.columns[idx] + + if column_name != "" and column_name not in self.temp_ordering: + self.add_to_sort(column_name) + + def remove_clicked(self): + """ + "Remove" button clicked + + Try to read a selection from both listboxes, giving priority to the left + one. + """ + idx = None + column_name = None + left_idx = self.available_listbox.curselection() + right_idx = self.sort_order_listbox.curselection() + if len(left_idx) > 0: + (idx,) = left_idx + + """ + In this case, `idx` corresponds to an index in the list of available + columns. + """ + column_name = self.columns[idx] + elif len(right_idx) > 0: + (idx,) = right_idx + + """ + In this case, `idx` corresponds to an index in the temporary + ordering. + """ + column_name = self.temp_ordering[idx] + else: + # Give up + return + + if column_name != "" and column_name in self.temp_ordering: + self.remove_from_sort(column_name) + + def move_up_clicked(self): + """ + "Move up" button clicked + """ + idx = self.sort_order_listbox.curselection() + if len(idx) <= 0: return + (idx,) = idx + + column_name = self.temp_ordering[idx] + + if column_name != "": + self.move_up(column_name) + + def move_down_clicked(self): + """ + "Move down" button clicked + """ + idx = self.sort_order_listbox.curselection() + if len(idx) <= 0: return + (idx,) = idx + + column_name = self.temp_ordering[idx] + + if column_name != "": + self.move_down(column_name) + + def reverse_clicked(self): + """ + "Reverse" button clicked + """ + idx = None + column_name = None + left_idx = self.available_listbox.curselection() + right_idx = self.sort_order_listbox.curselection() + if len(left_idx) > 0: + (idx,) = left_idx + + """ + In this case, `idx` corresponds to an index in the list of available + columns. + """ + column_name = self.columns[idx] + elif len(right_idx) > 0: + (idx,) = right_idx + + """ + In this case, `idx` corresponds to an index in the temporary + ordering. + """ + column_name = self.temp_ordering[idx] + else: + # Give up + return + + if column_name != "" and column_name in self.temp_ordering: + self.reverse(column_name) + if __name__ == '__main__': MasterWindow().mainloop() diff --git a/langs/Deutsch.py b/langs/Deutsch.py index 1db7829..399fcc3 100644 --- a/langs/Deutsch.py +++ b/langs/Deutsch.py @@ -43,4 +43,16 @@ TITLE_PER_MONTH = 'Monat' TITLE_PER_YEAR = 'Jahr' TITLE_FROM = 'Von' -TITLE_TO = 'Zu' \ No newline at end of file +TITLE_TO = 'Zu' +# For the multi-sort popup: NEEDS REVIEW +TITLE_MULTI_SORT = 'Mehrfachsortierung' +TITLE_CONFIGURE_MULTI_SORT = 'Mehrfachsortierung: Einstellungen' +TITLE_AVAILABLE_COLUMNS = 'Verfügbare Spalten' +TITLE_SORT_ORDER = 'Sortierreihenfolge' +TITLE_ADD = 'Hinzufügen' +TITLE_REMOVE = 'Entfernen' +TITLE_REVERSE = 'Umkehren' +TITLE_MOVE_UP = 'Nach oben' +TITLE_MOVE_DOWN = 'Nach unten' +TITLE_CLEAR = 'Zurücksetzen' +TITLE_APPLY = 'Anwenden' diff --git a/langs/English.py b/langs/English.py index 9d3cc50..0997000 100644 --- a/langs/English.py +++ b/langs/English.py @@ -44,3 +44,14 @@ TITLE_PER_YEAR = 'Year' TITLE_FROM = 'From' TITLE_TO = 'To' +TITLE_MULTI_SORT = 'Multi-Sort' +TITLE_CONFIGURE_MULTI_SORT = 'Configure Multi-Sort' +TITLE_AVAILABLE_COLUMNS = 'Available Columns' +TITLE_SORT_ORDER = 'Sort Order' +TITLE_ADD = 'Add' +TITLE_REMOVE = 'Remove' +TITLE_REVERSE = 'Reverse' +TITLE_MOVE_UP = 'Move up' +TITLE_MOVE_DOWN = 'Move down' +TITLE_CLEAR = 'Clear' +TITLE_APPLY = 'Apply' diff --git "a/langs/Espa\303\261ol.py" "b/langs/Espa\303\261ol.py" index 6700c40..bdbb4db 100644 --- "a/langs/Espa\303\261ol.py" +++ "b/langs/Espa\303\261ol.py" @@ -44,3 +44,15 @@ TITLE_PER_YEAR = 'Año' TITLE_FROM = 'De' TITLE_TO = 'A' +# For the multi-sort popup: NEEDS REVIEW +TITLE_MULTI_SORT = 'Clasificación múltiple' +TITLE_CONFIGURE_MULTI_SORT = 'Configurar clasificación múltiple' +TITLE_AVAILABLE_COLUMNS = 'Columnas disponibles' +TITLE_SORT_ORDER = 'Orden de clasificación' +TITLE_ADD = 'Agregar' +TITLE_REMOVE = 'Eliminar' +TITLE_REVERSE = 'Retroceder' +TITLE_MOVE_UP = 'Mover fila hacia arriba' +TITLE_MOVE_DOWN = 'Mover fila hacia abajo' +TITLE_CLEAR = 'Limpiar' +TITLE_APPLY = 'Aplicar' diff --git a/langs/Francais.py b/langs/Francais.py index 874c814..41cc9f7 100644 --- a/langs/Francais.py +++ b/langs/Francais.py @@ -44,3 +44,15 @@ TITLE_PER_YEAR = 'An' #R EVIEW NEEDED TITLE_FROM = 'De' TITLE_TO = 'À' +# For the multi-sort popup: NEEDS REVIEW +TITLE_MULTI_SORT = 'Tri multi-critères' +TITLE_CONFIGURE_MULTI_SORT = 'Configurer le tri multi-critères' +TITLE_AVAILABLE_COLUMNS = 'Colonnes disponibles' +TITLE_SORT_ORDER = 'Ordre de tri' +TITLE_ADD = 'Ajouter' +TITLE_REMOVE = 'Supprimer' +TITLE_REVERSE = 'Inverser' +TITLE_MOVE_UP = 'Remonter' +TITLE_MOVE_DOWN = 'Descendre' +TITLE_CLEAR = 'Effacer' +TITLE_APPLY = 'Appliquer' diff --git a/langs/Hindi.py b/langs/Hindi.py index 6cdad8f..abb2e39 100644 --- a/langs/Hindi.py +++ b/langs/Hindi.py @@ -43,4 +43,16 @@ TITLE_PER_MONTH = 'महीने' # REVIEW NEEDED TITLE_PER_YEAR = 'वर्ष' # REVIEW NEEDED TITLE_FROM = 'से' -TITLE_TO = 'तक' \ No newline at end of file +TITLE_TO = 'तक' +# For the multi-sort popup: NEEDS REVIEW +TITLE_MULTI_SORT = 'मल्टी-सॉर्ट' +TITLE_CONFIGURE_MULTI_SORT = 'मल्टी-सॉर्ट को कॉन्फ़िगर करें' +TITLE_AVAILABLE_COLUMNS = 'उपलब्ध कॉलम' +TITLE_SORT_ORDER = 'सॉर्ट क्रम' +TITLE_ADD = 'जोड़ें' +TITLE_REMOVE = 'हटाएं' +TITLE_REVERSE = 'उल्टा करें' +TITLE_MOVE_UP = 'ऊपर ले जाएं' +TITLE_MOVE_DOWN = 'नीचे ले जाएं' +TITLE_CLEAR = 'हटाएं' +TITLE_APPLY = 'लागू करें' diff --git a/langs/Marathi.py b/langs/Marathi.py index c857b74..a362be4 100644 --- a/langs/Marathi.py +++ b/langs/Marathi.py @@ -44,3 +44,15 @@ TITLE_PER_YEAR = 'वर्ष' TITLE_FROM = 'पासून' TITLE_TO = 'पर्यंत' +# For the multi-sort popup: NEEDS REVIEW +TITLE_MULTI_SORT = 'मल्टी-सॉर्ट' +TITLE_CONFIGURE_MULTI_SORT = 'मल्टी-सॉर्ट कॉन्फिगर करा' +TITLE_AVAILABLE_COLUMNS = 'उपलब्ध स्तंभ' +TITLE_SORT_ORDER = 'सॉर्ट क्रम' +TITLE_ADD = 'जोडा' +TITLE_REMOVE = 'काढा' +TITLE_REVERSE = 'उलटे करा' +TITLE_MOVE_UP = 'वर सरका' +TITLE_MOVE_DOWN = 'खाली सरका' +TITLE_CLEAR = 'हटा' +TITLE_APPLY = 'लागू करा' diff --git a/langs/Nederlands.py b/langs/Nederlands.py index 634445c..5b0103b 100644 --- a/langs/Nederlands.py +++ b/langs/Nederlands.py @@ -43,4 +43,16 @@ TITLE_PER_MONTH = 'Maand' TITLE_PER_YEAR = 'Jaar' TITLE_FROM = 'Van' -TITLE_TO = 'Naar' \ No newline at end of file +TITLE_TO = 'Naar' +# For the multi-sort popup: NEEDS REVIEW +TITLE_MULTI_SORT = 'Multi-sorteren' +TITLE_CONFIGURE_MULTI_SORT = 'Multi-sorteren configureren' +TITLE_AVAILABLE_COLUMNS = 'Beschikbare kolommen' +TITLE_SORT_ORDER = 'Sorteervolgorde' +TITLE_ADD = 'Toevoegen' +TITLE_REMOVE = 'Verwijderen' +TITLE_REVERSE = 'Omkeren' +TITLE_MOVE_UP = 'Omhoog verplaatsen' +TITLE_MOVE_DOWN = 'Omlaag verplaatsen' +TITLE_CLEAR = 'Wissen' +TITLE_APPLY = 'Toepassen' diff --git a/langs/Polski.py b/langs/Polski.py index 42c2cba..0f6e9cd 100644 --- a/langs/Polski.py +++ b/langs/Polski.py @@ -45,3 +45,15 @@ TITLE_PER_YEAR = 'Rocznie' TITLE_FROM = 'Od' TITLE_TO = 'Do' +# For the multi-sort popup: NEEDS REVIEW +TITLE_MULTI_SORT = 'Wielokrotne sortowanie' +TITLE_CONFIGURE_MULTI_SORT = 'Konfiguruj wielokrotne sortowanie' +TITLE_AVAILABLE_COLUMNS = 'Dostępne kolumny' +TITLE_SORT_ORDER = 'Kolejność sortowania' +TITLE_ADD = 'Dodaj' +TITLE_REMOVE = 'Usuń' +TITLE_REVERSE = 'Odwróć' +TITLE_MOVE_UP = 'Przesuń w górę' +TITLE_MOVE_DOWN = 'Przesuń w dół' +TITLE_CLEAR = 'Wyczyść' +TITLE_APPLY = 'Zastosuj' diff --git a/langs/Slovensky.py b/langs/Slovensky.py index 6d3c2d0..fac0484 100644 --- a/langs/Slovensky.py +++ b/langs/Slovensky.py @@ -44,3 +44,15 @@ TITLE_PER_YEAR = 'Rok' TITLE_FROM = 'Od' TITLE_TO = 'Do' +# For the multi-sort popup: NEEDS REVIEW +TITLE_MULTI_SORT = 'Viacnásobné zoradenie' +TITLE_CONFIGURE_MULTI_SORT = 'Konfigurácia viacnásobného zoradenia' +TITLE_AVAILABLE_COLUMNS = 'Dostupné stĺpce' +TITLE_SORT_ORDER = 'Poradie zoradenia' +TITLE_ADD = 'Pridať' +TITLE_REMOVE = 'Odstrániť' +TITLE_REVERSE = 'Obrátiť' +TITLE_MOVE_UP = 'Posunúť hore' +TITLE_MOVE_DOWN = 'Posunúť dole' +TITLE_CLEAR = 'Vymazať' +TITLE_APPLY = 'Použiť' diff --git a/langs/Tagalog.py b/langs/Tagalog.py index 961a11b..737b0c3 100644 --- a/langs/Tagalog.py +++ b/langs/Tagalog.py @@ -44,3 +44,15 @@ TITLE_PER_YEAR = 'Bawat taon' TITLE_FROM = 'Mula' TITLE_TO = 'Hanggang' +# For the multi-sort popup: NEEDS REVIEW +TITLE_MULTI_SORT = 'Multi-Uri' +TITLE_CONFIGURE_MULTI_SORT = 'I-configure ang Multi-Uri' +TITLE_AVAILABLE_COLUMNS = 'Magagamit na Mga Hanay' +TITLE_SORT_ORDER = 'Ayusin ang Order' +TITLE_ADD = 'Magdagdag' +TITLE_REMOVE = 'Tanggalin' +TITLE_REVERSE = 'Baligtarin' +TITLE_MOVE_UP = 'Ilipat Pataas' +TITLE_MOVE_DOWN = 'Ilipat Pababa' +TITLE_CLEAR = 'Linisin' +TITLE_APPLY = 'Ilapat' diff --git "a/langs/\316\225\316\273\316\273\316\267\316\275\316\271\316\272\316\254.py" "b/langs/\316\225\316\273\316\273\316\267\316\275\316\271\316\272\316\254.py" index a4e92da..2b433cb 100644 --- "a/langs/\316\225\316\273\316\273\316\267\316\275\316\271\316\272\316\254.py" +++ "b/langs/\316\225\316\273\316\273\316\267\316\275\316\271\316\272\316\254.py" @@ -44,3 +44,15 @@ TITLE_PER_YEAR = 'Ετος' # REVIEW NEEDED TITLE_FROM = 'Από' TITLE_TO = 'Προς' +# For the multi-sort popup: NEEDS REVIEW +TITLE_MULTI_SORT = 'Πολλαπλή Διαλογή' +TITLE_CONFIGURE_MULTI_SORT = 'Ρύθμιση Πολλαπλής Διαλογής' +TITLE_AVAILABLE_COLUMNS = 'Διαθέσιμες Στήλες' +TITLE_SORT_ORDER = 'Σειρά Διαλογής' +TITLE_ADD = 'Προσθήκη' +TITLE_REMOVE = 'Αφαίρεση' +TITLE_REVERSE = 'Αντιστροφή' +TITLE_MOVE_UP = 'Μετακίνηση Πάνω' +TITLE_MOVE_DOWN = 'Μετακίνηση Κάτω' +TITLE_CLEAR = 'Εκκαθάριση' +TITLE_APPLY = 'Εφαρμογή' diff --git "a/langs/\330\247\331\204\330\271\330\261\330\250\331\212\330\251.py" "b/langs/\330\247\331\204\330\271\330\261\330\250\331\212\330\251.py" index 18c7250..c505297 100644 --- "a/langs/\330\247\331\204\330\271\330\261\330\250\331\212\330\251.py" +++ "b/langs/\330\247\331\204\330\271\330\261\330\250\331\212\330\251.py" @@ -44,3 +44,15 @@ TITLE_PER_YEAR = 'السنة' TITLE_FROM = 'من' TITLE_TO = 'إلى' +# For the multi-sort popup: NEEDS REVIEW +TITLE_MULTI_SORT = 'فرز متعدد' +TITLE_CONFIGURE_MULTI_SORT = 'تكوين الفرز المتعدد' +TITLE_AVAILABLE_COLUMNS = 'الأعمدة المتاحة' +TITLE_SORT_ORDER = 'ترتيب الفرز' +TITLE_ADD = 'إضافة' +TITLE_REMOVE = 'إزالة' +TITLE_REVERSE = 'عكس' +TITLE_MOVE_UP = 'تحريك لأعلى' +TITLE_MOVE_DOWN = 'تحريك لأسفل' +TITLE_CLEAR = 'مسح' +TITLE_APPLY = 'تطبيق'