Merge Stable Changesto Master #9
@@ -1,5 +1,5 @@
 | 
				
			|||||||
# Python imports
 | 
					# Python imports
 | 
				
			||||||
import os, threading, subprocess, time, inspect
 | 
					import os, subprocess, time, inspect
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Lib imports
 | 
					# Lib imports
 | 
				
			||||||
import gi
 | 
					import gi
 | 
				
			||||||
@@ -10,19 +10,6 @@ from gi.repository import Gtk
 | 
				
			|||||||
from plugins.plugin_base import PluginBase
 | 
					from plugins.plugin_base import PluginBase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# NOTE: Threads WILL NOT die with parent's destruction.
 | 
					 | 
				
			||||||
def threaded(fn):
 | 
					 | 
				
			||||||
    def wrapper(*args, **kwargs):
 | 
					 | 
				
			||||||
        threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=False).start()
 | 
					 | 
				
			||||||
    return wrapper
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# NOTE: Threads WILL die with parent's destruction.
 | 
					 | 
				
			||||||
def daemon_threaded(fn):
 | 
					 | 
				
			||||||
    def wrapper(*args, **kwargs):
 | 
					 | 
				
			||||||
        threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start()
 | 
					 | 
				
			||||||
    return wrapper
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Plugin(PluginBase):
 | 
					class Plugin(PluginBase):
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,15 +1,17 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
<!-- Generated with glade 3.38.2 -->
 | 
					<!-- Generated with glade 3.40.0 -->
 | 
				
			||||||
<interface>
 | 
					<interface>
 | 
				
			||||||
  <requires lib="gtk+" version="3.24"/>
 | 
					  <requires lib="gtk+" version="3.24"/>
 | 
				
			||||||
  <object class="GtkListStore" id="favorites_store">
 | 
					  <object class="GtkListStore" id="favorites_store">
 | 
				
			||||||
    <columns>
 | 
					    <columns>
 | 
				
			||||||
      <!-- column-name Favorites -->
 | 
					      <!-- column-name Favorites -->
 | 
				
			||||||
      <column type="gchararray"/>
 | 
					      <column type="gchararray"/>
 | 
				
			||||||
 | 
					      <!-- column-name Path -->
 | 
				
			||||||
 | 
					      <column type="gchararray"/>
 | 
				
			||||||
    </columns>
 | 
					    </columns>
 | 
				
			||||||
  </object>
 | 
					  </object>
 | 
				
			||||||
  <object class="GtkDialog" id="favorites_dialog">
 | 
					  <object class="GtkDialog" id="favorites_dialog">
 | 
				
			||||||
    <property name="width-request">320</property>
 | 
					    <property name="width-request">420</property>
 | 
				
			||||||
    <property name="height-request">450</property>
 | 
					    <property name="height-request">450</property>
 | 
				
			||||||
    <property name="can-focus">False</property>
 | 
					    <property name="can-focus">False</property>
 | 
				
			||||||
    <property name="modal">True</property>
 | 
					    <property name="modal">True</property>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
# Python imports
 | 
					# Python imports
 | 
				
			||||||
import os, threading, subprocess, time, inspect, json
 | 
					import os, inspect, json
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Lib imports
 | 
					# Lib imports
 | 
				
			||||||
import gi
 | 
					import gi
 | 
				
			||||||
@@ -10,19 +10,6 @@ from gi.repository import Gtk
 | 
				
			|||||||
from plugins.plugin_base import PluginBase
 | 
					from plugins.plugin_base import PluginBase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# NOTE: Threads WILL NOT die with parent's destruction.
 | 
					 | 
				
			||||||
def threaded(fn):
 | 
					 | 
				
			||||||
    def wrapper(*args, **kwargs):
 | 
					 | 
				
			||||||
        threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=False).start()
 | 
					 | 
				
			||||||
    return wrapper
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# NOTE: Threads WILL die with parent's destruction.
 | 
					 | 
				
			||||||
def daemon_threaded(fn):
 | 
					 | 
				
			||||||
    def wrapper(*args, **kwargs):
 | 
					 | 
				
			||||||
        threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start()
 | 
					 | 
				
			||||||
    return wrapper
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Plugin(PluginBase):
 | 
					class Plugin(PluginBase):
 | 
				
			||||||
@@ -65,7 +52,8 @@ class Plugin(PluginBase):
 | 
				
			|||||||
            with open(self._FAVORITES_FILE) as f:
 | 
					            with open(self._FAVORITES_FILE) as f:
 | 
				
			||||||
                self._favorites = json.load(f)
 | 
					                self._favorites = json.load(f)
 | 
				
			||||||
                for favorite in self._favorites:
 | 
					                for favorite in self._favorites:
 | 
				
			||||||
                    self._favorites_store.append([favorite])
 | 
					                    display, path = favorite
 | 
				
			||||||
 | 
					                    self._favorites_store.append([display, path])
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            with open(self._FAVORITES_FILE, 'a') as f:
 | 
					            with open(self._FAVORITES_FILE, 'a') as f:
 | 
				
			||||||
                f.write('[]')
 | 
					                f.write('[]')
 | 
				
			||||||
@@ -78,25 +66,29 @@ class Plugin(PluginBase):
 | 
				
			|||||||
        button.connect("button-release-event", self._show_favorites_menu)
 | 
					        button.connect("button-release-event", self._show_favorites_menu)
 | 
				
			||||||
        return button
 | 
					        return button
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @threaded
 | 
					 | 
				
			||||||
    def _get_state(self, widget=None, eve=None):
 | 
					    def _get_state(self, widget=None, eve=None):
 | 
				
			||||||
        self._event_system.emit("get_current_state")
 | 
					        self._event_system.emit("get_current_state")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    @threaded
 | 
					 | 
				
			||||||
    def _set_current_dir_lbl(self, widget=None, eve=None):
 | 
					    def _set_current_dir_lbl(self, widget=None, eve=None):
 | 
				
			||||||
        self._current_dir_lbl.set_label(f"Current Directory:\n{self._fm_state.tab.get_current_directory()}")
 | 
					        self._current_dir_lbl.set_label(f"Current Directory:\n{self._fm_state.tab.get_current_directory()}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _add_to_favorite(self, state):
 | 
					    def _add_to_favorite(self, state):
 | 
				
			||||||
        current_directory = self._fm_state.tab.get_current_directory()
 | 
					        path    = self._fm_state.tab.get_current_directory()
 | 
				
			||||||
        self._favorites_store.append([current_directory])
 | 
					        parts   = path.split("/")
 | 
				
			||||||
        self._favorites.append(current_directory)
 | 
					        display = '/'.join(parts[-3:]) if len(parts) > 3 else path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self._favorites_store.append([display, path])
 | 
				
			||||||
 | 
					        self._favorites.append([display, path])
 | 
				
			||||||
        self._save_favorites()
 | 
					        self._save_favorites()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _remove_from_favorite(self, state):
 | 
					    def _remove_from_favorite(self, state):
 | 
				
			||||||
        path = self._favorites_store.get_value(self._selected, 0)
 | 
					        path = self._favorites_store.get_value(self._selected, 1)
 | 
				
			||||||
        self._favorites_store.remove(self._selected)
 | 
					        self._favorites_store.remove(self._selected)
 | 
				
			||||||
        self._favorites.remove(path)
 | 
					
 | 
				
			||||||
 | 
					        for i, f in enumerate(self._favorites):
 | 
				
			||||||
 | 
					            if f[1] == path:
 | 
				
			||||||
 | 
					                self._favorites.remove( self._favorites[i] )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self._save_favorites()
 | 
					        self._save_favorites()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _save_favorites(self):
 | 
					    def _save_favorites(self):
 | 
				
			||||||
@@ -104,7 +96,7 @@ class Plugin(PluginBase):
 | 
				
			|||||||
            json.dump(self._favorites, outfile, separators=(',', ':'), indent=4)
 | 
					            json.dump(self._favorites, outfile, separators=(',', ':'), indent=4)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _set_selected_path(self, widget=None, eve=None):
 | 
					    def _set_selected_path(self, widget=None, eve=None):
 | 
				
			||||||
        path = self._favorites_store.get_value(self._selected, 0)
 | 
					        path = self._favorites_store.get_value(self._selected, 1)
 | 
				
			||||||
        self._ui_objects[0].set_text(path)
 | 
					        self._ui_objects[0].set_text(path)
 | 
				
			||||||
        self._set_current_dir_lbl()
 | 
					        self._set_current_dir_lbl()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -119,5 +111,5 @@ class Plugin(PluginBase):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def _set_selected(self, user_data):
 | 
					    def _set_selected(self, user_data):
 | 
				
			||||||
        selected = user_data.get_selected()[1]
 | 
					        selected = user_data.get_selected()[1]
 | 
				
			||||||
        if selected:
 | 
					        if selected and not self._selected == selected:
 | 
				
			||||||
            self._selected = selected
 | 
					            self._selected = selected
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -115,6 +115,9 @@ class ShowHideMixin:
 | 
				
			|||||||
        if response == Gtk.ResponseType.CANCEL:
 | 
					        if response == Gtk.ResponseType.CANCEL:
 | 
				
			||||||
            self.cancel_edit = True
 | 
					            self.cancel_edit = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def show_io_popup(self, widget=None, eve=None):
 | 
				
			||||||
 | 
					        self.builder.get_object("io_popup").popup()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def hide_edit_file_menu(self, widget=None, eve=None):
 | 
					    def hide_edit_file_menu(self, widget=None, eve=None):
 | 
				
			||||||
        self.builder.get_object("edit_file_menu").hide()
 | 
					        self.builder.get_object("edit_file_menu").hide()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -320,16 +320,82 @@ class WidgetFileActionMixin:
 | 
				
			|||||||
                    if action == "move" or action == "rename":
 | 
					                    if action == "move" or action == "rename":
 | 
				
			||||||
                        tab.move_file(fPath, tPath)
 | 
					                        tab.move_file(fPath, tPath)
 | 
				
			||||||
                else:
 | 
					                else:
 | 
				
			||||||
 | 
					                    # if action == "copy":
 | 
				
			||||||
 | 
					                    #     file.copy(target, flags=Gio.FileCopyFlags.BACKUP, cancellable=None)
 | 
				
			||||||
 | 
					                    # if action == "move" or action == "rename":
 | 
				
			||||||
 | 
					                    #     file.move(target, flags=Gio.FileCopyFlags.BACKUP, cancellable=None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if action == "copy":
 | 
					                    if action == "copy":
 | 
				
			||||||
                        file.copy(target, flags=Gio.FileCopyFlags.BACKUP, cancellable=None)
 | 
					                        container, cancle_eve, update_progress, finish_callback = self.create_io_widget(action, file)
 | 
				
			||||||
 | 
					                        file.copy_async(destination=target, flags=Gio.FileCopyFlags.BACKUP,
 | 
				
			||||||
 | 
					                                        io_priority=98, cancellable=cancle_eve,
 | 
				
			||||||
 | 
					                                        progress_callback=update_progress, callback=finish_callback)
 | 
				
			||||||
 | 
					                        self.builder.get_object("io_list").add(container)
 | 
				
			||||||
                    if action == "move" or action == "rename":
 | 
					                    if action == "move" or action == "rename":
 | 
				
			||||||
                        file.move(target, flags=Gio.FileCopyFlags.BACKUP, cancellable=None)
 | 
					                        container, cancle_eve, update_progress, finish_callback = self.create_io_widget(action, file)
 | 
				
			||||||
 | 
					                        file.move(destination=target, flags=Gio.FileCopyFlags.BACKUP,
 | 
				
			||||||
 | 
					                                    cancellable=cancle_eve, progress_callback=update_progress)
 | 
				
			||||||
 | 
					                        self.builder.get_object("io_list").add(container)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            except GObject.GError as e:
 | 
					            except GObject.GError as e:
 | 
				
			||||||
                raise OSError(e)
 | 
					                raise OSError(e)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.exists_file_rename_bttn.set_sensitive(False)
 | 
					        self.exists_file_rename_bttn.set_sensitive(False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # NOTE: There is something not right about the way we are doing this.
 | 
				
			||||||
 | 
					    #       Calling cancel results in an error getting thrown to finish_callback
 | 
				
			||||||
 | 
					    #       and checking for task.had_error() is True and task.get_completed() is False
 | 
				
			||||||
 | 
					    def create_io_widget(self, action, file):
 | 
				
			||||||
 | 
					        cancle_eve  = Gio.Cancellable.new()
 | 
				
			||||||
 | 
					        container   = Gtk.Box()
 | 
				
			||||||
 | 
					        stats       = Gtk.Box()
 | 
				
			||||||
 | 
					        label       = Gtk.Label()
 | 
				
			||||||
 | 
					        progress    = Gtk.ProgressBar()
 | 
				
			||||||
 | 
					        cncl_button = Gtk.Button(label="Cancel")
 | 
				
			||||||
 | 
					        del_button  = Gtk.Button(label="Delete")
 | 
				
			||||||
 | 
					        io_list     = self.builder.get_object("io_list")
 | 
				
			||||||
 | 
					        label.set_label(file.get_basename())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        progress.set_show_text(True)
 | 
				
			||||||
 | 
					        progress.set_text(f"{action.upper()}ING")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def do_cancel(widget, container, eve):
 | 
				
			||||||
 | 
					            print(f"Canceling: [{action}] of {file.get_basename()} ...")
 | 
				
			||||||
 | 
					            eve.cancel()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def update_progress(current, total, eve=None):
 | 
				
			||||||
 | 
					            progress.set_fraction(current/total)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def finish_callback(file, task=None, eve=None):
 | 
				
			||||||
 | 
					            io_list.remove(container)
 | 
				
			||||||
 | 
					            # if not task.had_error():
 | 
				
			||||||
 | 
					            #     self.builder.get_object("io_list").remove(container)
 | 
				
			||||||
 | 
					            # else:
 | 
				
			||||||
 | 
					            #     print(f"{action} of {file.get_basename()} failed...")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def delete_container(widget, eve):
 | 
				
			||||||
 | 
					            io_list.remove(container)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not action == "move":
 | 
				
			||||||
 | 
					            stats.pack_end(cncl_button, False, False, 5)
 | 
				
			||||||
 | 
					            cncl_button.connect("clicked", do_cancel, *(container, cancle_eve))
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            stats.pack_end(del_button, False, False, 5)
 | 
				
			||||||
 | 
					            del_button.connect("clicked", delete_container, ())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        container.set_orientation(1)
 | 
				
			||||||
 | 
					        stats.set_orientation(0)
 | 
				
			||||||
 | 
					        stats.add(progress)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        container.add(label)
 | 
				
			||||||
 | 
					        container.add(stats)
 | 
				
			||||||
 | 
					        container.show_all()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return container, cancle_eve, update_progress, finish_callback
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def setup_exists_data(self, from_file, to_file):
 | 
					    def setup_exists_data(self, from_file, to_file):
 | 
				
			||||||
        from_info             = from_file.query_info("standard::*,time::modified", 0, cancellable=None)
 | 
					        from_info             = from_file.query_info("standard::*,time::modified", 0, cancellable=None)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
<!-- Generated with glade 3.38.2 -->
 | 
					<!-- Generated with glade 3.40.0 -->
 | 
				
			||||||
<interface>
 | 
					<interface>
 | 
				
			||||||
  <requires lib="gtk+" version="3.22"/>
 | 
					  <requires lib="gtk+" version="3.22"/>
 | 
				
			||||||
  <object class="GtkAboutDialog" id="about_page">
 | 
					  <object class="GtkAboutDialog" id="about_page">
 | 
				
			||||||
@@ -482,6 +482,11 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
 | 
				
			|||||||
    <property name="can-focus">False</property>
 | 
					    <property name="can-focus">False</property>
 | 
				
			||||||
    <property name="stock">gtk-execute</property>
 | 
					    <property name="stock">gtk-execute</property>
 | 
				
			||||||
  </object>
 | 
					  </object>
 | 
				
			||||||
 | 
					  <object class="GtkImage" id="io_img">
 | 
				
			||||||
 | 
					    <property name="visible">True</property>
 | 
				
			||||||
 | 
					    <property name="can-focus">False</property>
 | 
				
			||||||
 | 
					    <property name="stock">gtk-stop</property>
 | 
				
			||||||
 | 
					  </object>
 | 
				
			||||||
  <object class="GtkTextBuffer" id="message_buffer"/>
 | 
					  <object class="GtkTextBuffer" id="message_buffer"/>
 | 
				
			||||||
  <object class="GtkImage" id="rename_img">
 | 
					  <object class="GtkImage" id="rename_img">
 | 
				
			||||||
    <property name="visible">True</property>
 | 
					    <property name="visible">True</property>
 | 
				
			||||||
@@ -824,7 +829,23 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
 | 
				
			|||||||
                <property name="spacing">5</property>
 | 
					                <property name="spacing">5</property>
 | 
				
			||||||
                <property name="layout-style">start</property>
 | 
					                <property name="layout-style">start</property>
 | 
				
			||||||
                <child>
 | 
					                <child>
 | 
				
			||||||
                  <object class="GtkButton" id="plugins_buttoin">
 | 
					                  <object class="GtkButton" id="io_button">
 | 
				
			||||||
 | 
					                    <property name="label" translatable="yes">I/O</property>
 | 
				
			||||||
 | 
					                    <property name="visible">True</property>
 | 
				
			||||||
 | 
					                    <property name="can-focus">True</property>
 | 
				
			||||||
 | 
					                    <property name="receives-default">True</property>
 | 
				
			||||||
 | 
					                    <property name="image">io_img</property>
 | 
				
			||||||
 | 
					                    <property name="always-show-image">True</property>
 | 
				
			||||||
 | 
					                    <signal name="released" handler="show_io_popup" swapped="no"/>
 | 
				
			||||||
 | 
					                  </object>
 | 
				
			||||||
 | 
					                  <packing>
 | 
				
			||||||
 | 
					                    <property name="expand">True</property>
 | 
				
			||||||
 | 
					                    <property name="fill">True</property>
 | 
				
			||||||
 | 
					                    <property name="position">0</property>
 | 
				
			||||||
 | 
					                  </packing>
 | 
				
			||||||
 | 
					                </child>
 | 
				
			||||||
 | 
					                <child>
 | 
				
			||||||
 | 
					                  <object class="GtkButton" id="plugins_button">
 | 
				
			||||||
                    <property name="label" translatable="yes">Plugins</property>
 | 
					                    <property name="label" translatable="yes">Plugins</property>
 | 
				
			||||||
                    <property name="visible">True</property>
 | 
					                    <property name="visible">True</property>
 | 
				
			||||||
                    <property name="can-focus">True</property>
 | 
					                    <property name="can-focus">True</property>
 | 
				
			||||||
@@ -834,7 +855,7 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
 | 
				
			|||||||
                  <packing>
 | 
					                  <packing>
 | 
				
			||||||
                    <property name="expand">True</property>
 | 
					                    <property name="expand">True</property>
 | 
				
			||||||
                    <property name="fill">True</property>
 | 
					                    <property name="fill">True</property>
 | 
				
			||||||
                    <property name="position">0</property>
 | 
					                    <property name="position">1</property>
 | 
				
			||||||
                  </packing>
 | 
					                  </packing>
 | 
				
			||||||
                </child>
 | 
					                </child>
 | 
				
			||||||
                <child>
 | 
					                <child>
 | 
				
			||||||
@@ -850,7 +871,7 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
 | 
				
			|||||||
                  <packing>
 | 
					                  <packing>
 | 
				
			||||||
                    <property name="expand">True</property>
 | 
					                    <property name="expand">True</property>
 | 
				
			||||||
                    <property name="fill">True</property>
 | 
					                    <property name="fill">True</property>
 | 
				
			||||||
                    <property name="position">1</property>
 | 
					                    <property name="position">2</property>
 | 
				
			||||||
                  </packing>
 | 
					                  </packing>
 | 
				
			||||||
                </child>
 | 
					                </child>
 | 
				
			||||||
                <child>
 | 
					                <child>
 | 
				
			||||||
@@ -866,7 +887,7 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
 | 
				
			|||||||
                  <packing>
 | 
					                  <packing>
 | 
				
			||||||
                    <property name="expand">True</property>
 | 
					                    <property name="expand">True</property>
 | 
				
			||||||
                    <property name="fill">True</property>
 | 
					                    <property name="fill">True</property>
 | 
				
			||||||
                    <property name="position">2</property>
 | 
					                    <property name="position">3</property>
 | 
				
			||||||
                  </packing>
 | 
					                  </packing>
 | 
				
			||||||
                </child>
 | 
					                </child>
 | 
				
			||||||
                <child>
 | 
					                <child>
 | 
				
			||||||
@@ -882,7 +903,7 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
 | 
				
			|||||||
                  <packing>
 | 
					                  <packing>
 | 
				
			||||||
                    <property name="expand">True</property>
 | 
					                    <property name="expand">True</property>
 | 
				
			||||||
                    <property name="fill">True</property>
 | 
					                    <property name="fill">True</property>
 | 
				
			||||||
                    <property name="position">3</property>
 | 
					                    <property name="position">4</property>
 | 
				
			||||||
                  </packing>
 | 
					                  </packing>
 | 
				
			||||||
                </child>
 | 
					                </child>
 | 
				
			||||||
                <child>
 | 
					                <child>
 | 
				
			||||||
@@ -898,7 +919,7 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
 | 
				
			|||||||
                  <packing>
 | 
					                  <packing>
 | 
				
			||||||
                    <property name="expand">True</property>
 | 
					                    <property name="expand">True</property>
 | 
				
			||||||
                    <property name="fill">True</property>
 | 
					                    <property name="fill">True</property>
 | 
				
			||||||
                    <property name="position">4</property>
 | 
					                    <property name="position">5</property>
 | 
				
			||||||
                  </packing>
 | 
					                  </packing>
 | 
				
			||||||
                </child>
 | 
					                </child>
 | 
				
			||||||
              </object>
 | 
					              </object>
 | 
				
			||||||
@@ -1832,6 +1853,23 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
 | 
				
			|||||||
      <class name="alert-border"/>
 | 
					      <class name="alert-border"/>
 | 
				
			||||||
    </style>
 | 
					    </style>
 | 
				
			||||||
  </object>
 | 
					  </object>
 | 
				
			||||||
 | 
					  <object class="GtkPopover" id="io_popup">
 | 
				
			||||||
 | 
					    <property name="can-focus">False</property>
 | 
				
			||||||
 | 
					    <property name="relative-to">io_button</property>
 | 
				
			||||||
 | 
					    <property name="position">bottom</property>
 | 
				
			||||||
 | 
					    <child>
 | 
				
			||||||
 | 
					      <object class="GtkBox" id="io_list">
 | 
				
			||||||
 | 
					        <property name="width-request">320</property>
 | 
				
			||||||
 | 
					        <property name="height-request">320</property>
 | 
				
			||||||
 | 
					        <property name="visible">True</property>
 | 
				
			||||||
 | 
					        <property name="can-focus">False</property>
 | 
				
			||||||
 | 
					        <property name="orientation">vertical</property>
 | 
				
			||||||
 | 
					        <child>
 | 
				
			||||||
 | 
					          <placeholder/>
 | 
				
			||||||
 | 
					        </child>
 | 
				
			||||||
 | 
					      </object>
 | 
				
			||||||
 | 
					    </child>
 | 
				
			||||||
 | 
					  </object>
 | 
				
			||||||
  <object class="GtkPopover" id="message_popup_widget">
 | 
					  <object class="GtkPopover" id="message_popup_widget">
 | 
				
			||||||
    <property name="width-request">320</property>
 | 
					    <property name="width-request">320</property>
 | 
				
			||||||
    <property name="can-focus">False</property>
 | 
					    <property name="can-focus">False</property>
 | 
				
			||||||
@@ -2099,7 +2137,7 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
 | 
				
			|||||||
  </object>
 | 
					  </object>
 | 
				
			||||||
  <object class="GtkPopover" id="plugin_controls">
 | 
					  <object class="GtkPopover" id="plugin_controls">
 | 
				
			||||||
    <property name="can-focus">False</property>
 | 
					    <property name="can-focus">False</property>
 | 
				
			||||||
    <property name="relative-to">plugins_buttoin</property>
 | 
					    <property name="relative-to">plugins_button</property>
 | 
				
			||||||
    <signal name="button-release-event" handler="hide_plugins_popup" swapped="no"/>
 | 
					    <signal name="button-release-event" handler="hide_plugins_popup" swapped="no"/>
 | 
				
			||||||
    <signal name="key-release-event" handler="hide_plugins_popup" swapped="no"/>
 | 
					    <signal name="key-release-event" handler="hide_plugins_popup" swapped="no"/>
 | 
				
			||||||
    <child>
 | 
					    <child>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user