# -*- coding: utf-8 -*-
# Copyright (C) 2007 daelstorm. All rights reserved.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
# Original copyright below
# Copyright (c) 2003-2004 Hyriand. All rights reserved.
import gtk
import gobject, pango
import re
import sre_constants
import locale
import string
import random
from pynicotine import slskmessages
from utils import InitialiseColumns, IconNotebook, PopupMenu, FastListModel, Humanize, HumanSpeed, HumanSize, PressHeader
from dirchooser import ChooseDir
from entrydialog import *
from pynicotine.utils import _
from utils import InputDialog, showCountryTooltip
from pynicotine.logfacility import log
from time import time
class WishList( gtk.Dialog):
def __init__(self, frame):
gtk.Dialog.__init__(self)
self.set_title(_("Nicotine+ Wishlist"))
self.set_icon(frame.images["n"])
self.connect("destroy", self.quit)
self.connect("destroy-event", self.quit)
self.connect("delete-event", self.quit)
self.connect("delete_event", self.quit)
self.nicotine = frame
self.set_size_request(250, 250)
self.mainHbox = gtk.HBox(False, 5)
self.mainHbox.show()
self.WishLabel = gtk.Label(_("Search Wishlist"))
self.WishLabel.set_padding(0, 0)
self.WishLabel.set_line_wrap(False)
self.WishLabel.show()
self.vbox.pack_start(self.WishLabel, False, True, 0)
self.WishScrollWin = gtk.ScrolledWindow()
self.WishScrollWin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
self.WishScrollWin.show()
self.WishScrollWin.set_shadow_type(gtk.SHADOW_IN)
self.WishlistView = gtk.TreeView()
self.WishlistView.show()
self.WishlistView.set_headers_visible(False)
self.WishScrollWin.add(self.WishlistView)
self.mainHbox.pack_start(self.WishScrollWin, True, True, 0)
self.mainVbox = gtk.VBox(False, 5)
self.mainHbox.pack_start(self.mainVbox, False, False)
self.mainVbox.show()
self.mainVbox.set_spacing(5)
self.AddWishButton = self.nicotine.CreateIconButton(gtk.STOCK_ADD, "stock", self.OnAddWish, _("Add..."))
self.mainVbox.pack_start(self.AddWishButton, False, False, 0)
self.RemoveWishButton = self.nicotine.CreateIconButton(gtk.STOCK_REMOVE, "stock", self.OnRemoveWish, _("Remove"))
self.mainVbox.pack_start(self.RemoveWishButton, False, False, 0)
self.SelectAllWishesButton = self.nicotine.CreateIconButton(gtk.STOCK_DND_MULTIPLE, "stock", self.OnSelectAllWishes, _("Select all"))
self.mainVbox.pack_start(self.SelectAllWishesButton, False, False, 0)
self.CloseButton = self.nicotine.CreateIconButton(gtk.STOCK_CLOSE, "stock", self.quit, _("Close"))
self.mainVbox.pack_end(self.CloseButton, False, False, 0)
self.vbox.pack_start(self.mainHbox, True, True, 0)
self.store = gtk.ListStore(gobject.TYPE_STRING)
column = gtk.TreeViewColumn(_("Wishs"), gtk.CellRendererText(), text = 0)
self.WishlistView.append_column(column)
self.WishlistView.set_model(self.store)
self.WishlistView.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
column.set_sort_column_id(0)
self.store.set_sort_column_id(0, gtk.SORT_ASCENDING)
self.wishes = {}
for wish in self.nicotine.np.config.sections["server"]["autosearch"]:
self.wishes[wish] = self.store.append([wish])
def OnAddWish(self, widget):
wish = InputDialog(self.vbox.get_toplevel(), _("Add Wish..."), _("Wish:") )
if wish and wish not in self.wishes:
self.wishes[wish] = self.store.append([wish])
self.nicotine.Searches.NewWish(wish)
def _RemoveWish(self, model, path, iter, l):
l.append(iter)
def removeWish(self, wish):
if wish in self.wishes:
self.store.remove(self.wishes[wish])
del self.wishes[wish]
if wish in self.nicotine.np.config.sections["server"]["autosearch"]:
self.nicotine.np.config.sections["server"]["autosearch"].remove(wish)
for number, search in self.nicotine.Searches.searches.items():
if search[1] == wish and search[4] == 1:
search[4] = 0
self.nicotine.Searches.searches[number] = search
if search[2] is not None:
search[2].RememberCheckButton.set_active(False)
break
def addWish(self, wish):
if wish and wish not in self.wishes:
self.wishes[wish] = self.store.append([wish])
def OnRemoveWish(self, widget):
iters = []
self.WishlistView.get_selection().selected_foreach(self._RemoveWish, iters)
for iter in iters:
wish = self.store.get_value(iter, 0)
self.removeWish(wish)
def OnSelectAllWishes(self, widget):
self.WishlistView.get_selection().select_all()
def quit(self, w=None, event=None):
self.hide()
return True
def Toggle(self, widget):
if self.get_property("visible"):
self.hide()
else:
self.show()
class Searches(IconNotebook):
def __init__(self, frame):
self.frame = frame
self.interval = 0
self.searchid = int(random.random() * (2**31-1))
self.searches = {}
self.usersearches = {}
self.users = {}
self.timer = None
self.disconnected = 0
ui = self.frame.np.config.sections["ui"]
IconNotebook.__init__(self, frame.images, ui["labelprivate"], ui["tabclosers"], ui["tab_icons"], ui["tab_reorderable"])
self.popup_enable()
#frame.SearchEntryCombo.disable_activate()
self.WishListDialog = WishList(frame)
self.UpdateColours()
self.show()
def LoadConfig(self):
"""
Add search history to SearchEntryCombo later and connect Wishlist,
after widgets have been created by glade. (doing this in __init__ causes tracebacks
during import with gtk.glade.XML in frame.py)
"""
items = self.frame.np.config.sections["searches"]["history"]
templist = []
for i in items:
if i not in templist:
templist.append(i)
for i in templist:
self.frame.SearchEntryCombo.append_text(i)
self.frame.WishList.connect("clicked", self.WishListDialog.Toggle)
def SetInterval(self, msg):
self.interval = msg.seconds
if not self.disconnected:
# Create wishlist searches (without tabs)
for term in self.frame.np.config.sections["server"]["autosearch"]:
self.searches[self.searchid] = [self.searchid, term, None, 0, True]
self.searchid = (self.searchid + 1) % (2**31)
self.OnAutoSearch()
self.timer = gobject.timeout_add(self.interval*1000, self.OnAutoSearch)
def ConnClose(self):
self.disconnected = 1
if self.timer is not None:
gobject.source_remove(self.timer)
self.timer = None
def OnAutoSearch(self, *args):
# Wishlists supported by server?
if self.interval == 0:
log.addwarning("The server forbid us from doing wishlist searches.")
return False
searches = self.frame.np.config.sections["server"]["autosearch"]
if not searches:
return True
# Search for a maximum of 3 items at each search interval
for term in searches[0:3]:
for i in self.searches.values():
if i[1] == term and i[4]:
self.DoWishListSearch(i[0], term)
oldsearch = searches.pop()
searches.insert(0, oldsearch)
return True
def OnClearSearchHistory(self):
self.frame.SearchEntry.set_text("")
self.frame.np.config.sections["searches"]["history"] = []
self.frame.np.config.writeConfiguration()
self.frame.SearchEntryCombo.get_model().clear()
self.frame.SearchEntryCombo.append_text("")
def OnSearch(self):
text = self.frame.SearchEntry.get_text().strip()
if not text:
return
users = []
room = None
if self.frame.SearchMethod.get_active_text() == _("Global"):
mode = 0
elif self.frame.SearchMethod.get_active_text() == _("Rooms"):
mode = 1
name = self.frame.RoomSearchCombo.child.get_text()
# Space after Joined Rooms is important, so it doesn't conflict
# with any possible real room
if name != _("Joined Rooms ") and not name.isspace():
room = name
elif self.frame.SearchMethod.get_active_text() == _("Buddies"):
mode = 2
elif self.frame.SearchMethod.get_active_text() == _("User"):
mode = 3
user = self.frame.UserSearchCombo.child.get_text().strip()
if user != "" and not user.isspace():
users = [user]
else:
return
else:
mode = 0
feedback = None
if mode == 0:
feedback = self.frame.pluginhandler.OutgoingGlobalSearchEvent(text)
if feedback != None:
text = feedback[0]
elif mode == 1:
feedback = self.frame.pluginhandler.OutgoingRoomSearchEvent(room, text)
if feedback != None:
(room, text) = feedback
elif mode == 2:
feedback = self.frame.pluginhandler.OutgoingBuddySearchEvent(text)
if feedback != None:
text = feedback[0]
elif mode == 3:
feedback = self.frame.pluginhandler.OutgoingUserSearchEvent(users)
if feedback != None:
users = feedback[0]
else:
print "Unknown search mode, not using plugin system. Fix me!"
feedback = True
if feedback != None:
self.DoSearch(text, mode, users, room)
self.frame.SearchEntry.set_text("")
def NewWish(self, wish):
if wish in self.frame.np.config.sections["server"]["autosearch"]:
return
self.frame.np.config.sections["server"]["autosearch"].append(wish)
self.searchid += 1
self.searches[self.searchid] = [self.searchid, wish, None, 0, True]
self.DoWishListSearch(self.searchid, wish)
def DoSearch(self, text, mode, users = [], room = None):
items = self.frame.np.config.sections["searches"]["history"]
if text in items:
items.remove(text)
items.insert(0, text)
# Clear old items
del items[15:]
self.frame.np.config.writeConfiguration()
# Repopulate the combo list
self.frame.SearchEntryCombo.get_model().clear()
templist = []
for i in items:
if i not in templist:
templist.append(i)
for i in templist:
self.frame.SearchEntryCombo.append_text(i)
if mode == 3 and users != [] and users[0] != '':
self.usersearches[self.searchid] = users
search = self.CreateTab(self.searchid, text, mode)
if search[2] is not None:
self.set_current_page(self.page_num(search[2].Main))
text = self.frame.np.encode(text)
if mode == 0:
self.DoGlobalSearch(self.searchid, text)
elif mode == 1:
self.DoRoomsSearch(self.searchid, text, room)
elif mode == 2:
self.DoBuddiesSearch(self.searchid, text)
elif mode == 3 and users != [] and users[0] != '':
self.DoPeerSearch(self.searchid, text, users)
self.searchid += 1
def DoGlobalSearch(self, id, text):
self.frame.np.queue.put(slskmessages.FileSearch(id, text))
def DoWishListSearch(self, id, text):
self.frame.np.queue.put(slskmessages.WishlistSearch(id, text))
def DoRoomsSearch(self, id, text, room = None):
if room != None:
self.frame.np.queue.put(slskmessages.RoomSearch(room, id, text))
else:
for room in self.frame.chatrooms.roomsctrl.joinedrooms.keys():
self.frame.np.queue.put(slskmessages.RoomSearch(room, id, text))
def DoBuddiesSearch(self, id, text):
for users in self.frame.userlist.userlist:
self.frame.np.queue.put(slskmessages.UserSearch(users[0], id, text))
def DoPeerSearch(self, id, text, users):
for user in users:
self.frame.np.ProcessRequestToPeer(user, slskmessages.FileSearchRequest(None,id,text))
def GetUserSearchName(self, id):
if id in self.usersearches:
users = self.usersearches[id]
if len(users) > 1:
return _("Users")
elif len(users) == 1:
return users[0]
return _("User")
def CreateTab(self, id, text, mode, remember = False):
tab = Search(self, text, id, mode, remember)
if mode:
label = "(" + ("", _("Rooms"), _("Buddies"), self.GetUserSearchName(id))[mode] + ") " + text[:15]
else:
label = text[:20]
self.append_page(tab.Main, label, tab.OnClose)
search = [id, text, tab, mode, remember]
self.searches[id] = search
return search
def ShowResult(self, msg, username, country):
if msg.token not in self.searches:
return
search = self.searches[msg.token]
if search[2] == None:
search = self.CreateTab(search[0], search[1], search[3], search[4])
search[2].AddResult(msg, username, country)
def RemoveAutoSearch(self, id):
if not id in self.searches:
return
search = self.searches[id]
if search[1] in self.frame.np.config.sections["server"]["autosearch"]:
self.frame.np.config.sections["server"]["autosearch"].remove(search[1])
self.frame.np.config.writeConfiguration()
search[4] = 0
self.WishListDialog.removeWish(search[1])
def RemoveTab(self, tab):
import gc
if tab.id in self.searches:
search = self.searches[tab.id]
search[2] = None
#if search[4]:
#self.RemoveAutoSearch(search[0])
self.remove_page(tab.Main)
tab.Main.destroy()
for i in tab.__dict__.keys():
del tab.__dict__[i]
gc.collect()
def AutoSearch(self, id):
if id not in self.searches:
return
i = self.searches[id]
if i[1] in self.frame.np.config.sections["server"]["autosearch"]:
return
self.frame.np.config.sections["server"]["autosearch"].append(i[1])
self.frame.np.config.writeConfiguration()
i[4] = 1
self.WishListDialog.addWish(i[1])
def UpdateColours(self):
for id in self.searches.values():
if id[2] is None:
continue
id[2].ChangeColours()
self.frame.SetTextBG(self.WishListDialog.WishlistView)
def saveColumns(self):
page_num = self.get_current_page()
if page_num is not None:
page = self.get_nth_page(page_num)
for name, search in self.searches.items():
if search[2] is None:
continue
if search[2].Main == page:
search[2].saveColumns()
break
def GetUserStatus(self, msg):
for number, search in self.searches.items():
if search[2] is None:
continue
search[2].GetUserStatus(msg)
def NonExistantUser(self, user):
for number, search in self.searches.items():
if search[2] is None:
continue
search[2].NonExistantUser(user)
def TabPopup(self, id):
popup = PopupMenu(self.frame)
popup.setup(
("#" + _("Detach this tab"), self.searches[id][2].Detach, gtk.STOCK_REDO),
("#" + _("Close this tab"), self.searches[id][2].OnClose, gtk.STOCK_CLOSE),
)
items = popup.get_children()
return popup
def on_tab_click(self, widget, event, child):
if event.type == gtk.gdk.BUTTON_PRESS:
id = None
n = self.page_num(child)
page = self.get_nth_page(n)
for search, data in self.searches.items():
if data[2] is None:
continue
if data[2].Main is page:
id = search
break
if id is None:
print "ID is none"
return
if event.button == 2:
self.searches[id][2].OnClose(widget)
return True
if event.button == 3:
menu = self.TabPopup(id)
menu.popup(None, None, None, event.button, event.time)
return True
return False
class Search:
WAIT_BEFORE_DISPLAYING = 5000 # in milliseconds
def __init__(self, Searches, text, id, mode, remember):
self.Searches = Searches
self.frame = Searches.frame
#self.tooltips = self.frame.tooltips
#if not self.frame.np.config.sections["ui"]["tooltips"]:
# self.tooltips.disable()
self.wTree = gtk.glade.XML(os.path.join(os.path.dirname(os.path.realpath(__file__)), "search.glade" ), None, 'nicotine' )
widgets = self.wTree.get_widget_prefix("")
for i in widgets:
name = gtk.glade.get_widget_name(i)
self.__dict__[name] = i
self.SearchTab.remove(self.Main)
self.SearchTab.destroy()
self.wTree.signal_autoconnect(self)
self.FilterBitrate_List = gtk.ListStore(gobject.TYPE_STRING)
self.FilterBitrate.set_model(self.FilterBitrate_List)
self.FilterBitrate.set_text_column(0)
self.FilterSize_List = gtk.ListStore(gobject.TYPE_STRING)
self.FilterSize.set_model(self.FilterSize_List)
self.FilterSize.set_text_column(0)
self.FilterCountry_List = gtk.ListStore(gobject.TYPE_STRING)
self.FilterCountry.set_model(self.FilterCountry_List)
self.FilterCountry.set_text_column(0)
self.FilterIn_List = gtk.ListStore(gobject.TYPE_STRING)
self.FilterIn.set_model(self.FilterIn_List)
self.FilterIn.set_text_column(0)
self.FilterOut_List = gtk.ListStore(gobject.TYPE_STRING)
self.FilterOut.set_model(self.FilterOut_List)
self.FilterOut.set_text_column(0)
self.wTree.signal_autoconnect(self)
# self.ResultsList.set_double_buffered(False)
self.text = text
self.id = id
self.mode = mode
self.remember = remember
self.usersiters = {}
self.users = []
self.resultslimit = 2000
self.QueryLabel.set_text(text)
self.all_data = []
self.filters = None
# num, user, filename, h_size, h_speed, h_queue, immediatedl, h_bitrate, length, self.get_flag(user, country), directory, bitrate, fullpath, country, size, speed, queue, status]
self.COLUMN_TYPES = [int, str, str, str, str, str, str, str, str, gtk.gdk.Pixbuf, str,
int, str, str, gobject.TYPE_UINT64, int, int, int]
self.resultsmodel = gtk.TreeStore(* self.COLUMN_TYPES )
if mode > 0:
self.RememberCheckButton.set_sensitive(False)
self.RememberCheckButton.set_active(remember)
self.PopulateFilters()
self.FilterSize.clear()
sizecell = gtk.CellRendererText()
sizecell.set_property("xalign", 1)
self.FilterSize.pack_start(sizecell, True)
self.FilterSize.add_attribute(sizecell, "text", 0)
self.FilterBitrate.clear()
bit_cell = gtk.CellRendererText()
bit_cell.set_property("xalign", 1)
self.FilterBitrate.pack_start(bit_cell, True)
self.FilterBitrate.add_attribute(bit_cell, "text", 0)
self.FilterIn.connect("changed", self.OnFilterChanged)
self.FilterOut.connect("changed", self.OnFilterChanged)
self.FilterSize.connect("changed", self.OnFilterChanged)
self.FilterBitrate.connect("changed", self.OnFilterChanged)
self.FilterCountry.connect("changed", self.OnFilterChanged)
self.FilterIn.child.connect("activate", self.OnRefilter)
self.FilterOut.child.connect("activate", self.OnRefilter)
self.FilterSize.child.connect("activate", self.OnRefilter)
self.FilterBitrate.child.connect("activate", self.OnRefilter)
self.FilterCountry.child.connect("activate", self.OnRefilter)
self.selected_results = []
self.selected_users = []
self.ResultsList.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
self.ResultsList.set_property("show-expanders", False)
self.ResultsList.set_property("rules-hint", True)
cols = InitialiseColumns(self.ResultsList,
[_("Number"), 50, "number", self.CellDataFunc],
[_("User"), 100, "text", self.CellDataFunc],
[_("Filename"), 250, "text", self.CellDataFunc],
[_("Size"), 100, "number", self.CellDataFunc],
[_("Speed"), 90, "text", self.CellDataFunc],
[_("In queue"), 50, "number", self.CellDataFunc],
[_("Immediate Download"), 20, "text", self.CellDataFunc],
[_("Bitrate"), 50, "text", self.CellDataFunc],
[_("Length"), 50, "number", self.CellDataFunc],
[_("Country"), 25, "pixbuf"],
[_("Directory"), 1000, "text", self.CellDataFunc],
)
self.col_num, self.col_user, self.col_file, self.col_size, self.col_speed, self.col_queue, self.col_immediate, self.col_bitrate, self.col_length, self.col_country, self.col_directory = cols
cols[0].get_widget().hide()
for i in range (10):
parent = cols[i].get_widget().get_ancestor(gtk.Button)
if parent:
parent.connect('button_press_event', PressHeader)
# Read Show / Hide column settings from last session
cols[i].set_visible(self.frame.np.config.sections["columns"]["search"][i])
self.ResultsList.set_model(self.resultsmodel)
#for ix in range(len(cols)):
#col = cols[ix]
#col.connect("clicked", self.OnResort, ix)
#for r in col.get_cell_renderers():
#r.set_fixed_height_from_font(1)
#for r in self.col_num.get_cell_renderers() + self.col_size.get_cell_renderers() + self.col_length.get_cell_renderers():
#r.set_property("xalign",1)
self.col_num.set_sort_column_id(0)
self.col_user.set_sort_column_id(1)
self.col_file.set_sort_column_id(2)
self.col_size.set_sort_column_id(14)
self.col_speed.set_sort_column_id(15)
self.col_queue.set_sort_column_id(16)
self.col_immediate.set_sort_column_id(6)
self.col_bitrate.set_sort_column_id(11)
self.col_length.set_sort_column_id(8)
self.col_country.set_sort_column_id(13)
self.col_directory.set_sort_column_id(10)
if gtk.pygtk_version[0] >= 2 and gtk.pygtk_version[1] >= 10:
self.ResultsList.set_enable_tree_lines(True)
self.ResultsList.set_headers_clickable(True)
self.popup_menu_users = PopupMenu(self.frame)
self.popup_menu = popup = PopupMenu(self.frame)
popup.setup(
("#" + _("_Download file(s)"), self.OnDownloadFiles, gtk.STOCK_GO_DOWN),
("#" + _("Download file(s) _to..."), self.OnDownloadFilesTo, gtk.STOCK_GO_DOWN),
("#" + _("Download containing _folder(s)"), self.OnDownloadFolders, gtk.STOCK_GO_DOWN),
("#" + _("Download containing f_older(s) to..."), self.OnDownloadFoldersTo, gtk.STOCK_GO_DOWN),
("#" + _("View Metadata of file(s)"), self.OnSearchMeta, gtk.STOCK_PROPERTIES),
("", None),
("#" + _("Copy _URL"), self.OnCopyURL, gtk.STOCK_COPY),
("#" + _("Copy folder U_RL"), self.OnCopyDirURL, gtk.STOCK_COPY),
("", None),
(1, _("User(s)"), self.popup_menu_users, self.OnPopupMenuUsers),
)
self.ResultsList.connect("button_press_event", self.OnListClicked)
self._more_results = 0
self.new_results = []
self.ChangeColours()
def OnTooltip(self, widget, x, y, keyboard_mode, tooltip):
return showCountryTooltip(widget, x, y, tooltip, 13, stripprefix='')
def OnFilterChanged(self, widget):
model = widget.get_model()
iter = widget.get_active_iter()
if iter:
self.OnRefilter(None)
def PopulateFilters(self):
if self.frame.np.config.sections["searches"]["enablefilters"]:
filter = self.frame.np.config.sections["searches"]["defilter"]
self.FilterIn.child.set_text(filter[0])
self.FilterOut.child.set_text(filter[1])
self.FilterSize.child.set_text(filter[2])
self.FilterBitrate.child.set_text(filter[3])
self.FilterFreeSlot.set_active(filter[4])
if(len(filter) > 5):
self.FilterCountry.child.set_text(filter[5])
self.filtersCheck.set_active(1)
for i in ['0', '128', '160', '192', '256', '320']:
self.FilterBitrate.get_model().append([i])
for i in [">10MiB", "<10MiB", "<5MiB", "<1MiB", ">0"]:
self.FilterSize.get_model().append([i])
s_config = self.frame.np.config.sections["searches"]
for i in s_config["filterin"]:
self.AddCombo(self.FilterIn, i, True)
for i in s_config["filterout"]:
self.AddCombo(self.FilterOut, i, True)
for i in s_config["filtersize"]:
self.AddCombo(self.FilterSize, i, True)
for i in s_config["filterbr"]:
self.AddCombo(self.FilterBitrate, i, True)
for i in s_config["filtercc"]:
self.AddCombo(self.FilterCountry ,i, True)
def AddCombo(self, ComboboxEntry, text, list=False):
text = text.strip()
if not text:
return False
model = ComboboxEntry.get_model()
iter = model.get_iter_root()
match = False
while iter is not None:
value = model.get_value(iter, 0)
if value.strip() == text:
match = True
iter = model.iter_next(iter)
if not match:
if list:
model.append([text])
else:
model.prepend([text])
def Attach(self, widget=None):
self.Searches.attach_tab(self.Main)
def Detach(self, widget=None):
self.Searches.detach_tab(self.Main, _("Nicotine+ %s Search: %s") % ([_("Global"), _("Rooms"), _("Buddies"), self.Searches.GetUserSearchName(self.id)][self.mode], self.text))
def AddResult(self, msg, user, country):
if user in self.users:
return
if user not in self.Searches.users:
if user in self.frame.np.users and self.frame.np.users[user].status is not None:
self.Searches.users[user] = self.frame.np.users[user].status
else:
self.Searches.users[user] = 0
self.frame.np.queue.put(slskmessages.AddUser(user))
if user == self.frame.np.config.sections["server"]["login"]:
self.Searches.users[user] = 1
self.users.append(user)
results = []
if msg.freeulslots:
imdl = _("Y")
else:
imdl = _("N")
decode = self.frame.np.decode
for result in msg.list:
name = result[1].split('\\')[-1]
dir = result[1][:-len(name)]
bitrate = ""
length = ""
br = 0
if result[3] != "" and len(result[4]) == 3:
a = result[4]
if a[2] == 1:
bitrate = _(" (vbr)")
bitrate = str(a[0]) + bitrate
br = a[0]
length = '%i:%02i' %(a[1] / 60, a[1] % 60)
results.append([user, name, result[2], msg.ulspeed, msg.inqueue, imdl, bitrate, length, dir, br, result[1], country, self.Searches.users[user]])
if results:
self.new_results += results
if self._more_results == 0 and len(self.resultsmodel) < self.frame.np.config.sections['searches']["max_displayed_results"]:
self._more_results = 1
if len(self.resultsmodel) < 25: # Showing the first 50 results right away, buffering the rest
gobject.timeout_add(0, self._realaddresults)
else:
gobject.timeout_add(self.WAIT_BEFORE_DISPLAYING, self._realaddresults)
return len(results)
def _realaddresults(self):
if "_more_results" not in self.__dict__:
return
self._more_results = 0
r = self.new_results
self.new_results = []
res = self.append(r)
if res:
self.frame.Searches.request_changed(self.Main)
if self.frame.MainNotebook.get_current_page() != self.frame.MainNotebook.page_num(self.frame.searchvbox):
self.frame.SearchTabLabel.child.set_image(self.frame.images["online"])
#self.frame.RequestIcon(self.frame.SearchTabLabel)
rows = len(self.all_data)
for c in self.ResultsList.get_columns():
for r in c.get_cell_renderers():
if type(r) is not gtk.CellRendererPixbuf:
r.set_fixed_height_from_font(1)
return False
def get_flag(self, user, flag=None):
#flag = users[user].country
if flag is not None:
flag = "flag_"+flag
self.frame.flag_users[user] = flag
else:
flag = self.frame.GetUserFlag(user)
return self.frame.GetFlagImage(flag)
def append(self, results):
itercounter = len(self.all_data) + 1
displaycounter = len(self.resultsmodel)
encode = self.frame.np.encodeuser
returned = 0
if itercounter > self.frame.np.config.sections['searches']["max_stored_results"]:
return returned
for r in results:
user, filename, size, speed, queue, immediatedl, h_bitrate, length, directory, bitrate, fullpath, country, status = r
if user in self.Searches.users and status != self.Searches.users[user]:
status = self.Searches.users[user]
if status is None:
status = 0
h_size = HumanSize(long(size))
h_speed = HumanSpeed(speed)
h_queue = Humanize(queue)
if self.usersGroup.get_active() and user not in self.usersiters:
self.usersiters[user] = self.resultsmodel.append(None, [0, user, "", "", h_speed, h_queue, immediatedl, "", "", self.get_flag(user, country), "", 0, "", country, 0, speed, queue, status])
row = [itercounter, user, filename, h_size, h_speed, h_queue, immediatedl, h_bitrate, length,
directory, bitrate, fullpath, country, size, speed, queue, status]
self.all_data.append(row)
if (displaycounter + returned < self.frame.np.config.sections['searches']["max_displayed_results"]) and (not self.filters or self.check_filter(row)):
encoded_row = [itercounter, user, encode(filename, user), h_size, h_speed, h_queue, immediatedl, h_bitrate, length, self.get_flag(user, country), encode(directory, user), bitrate, encode(fullpath, user), country, size, speed, queue, status]
try:
if user in self.usersiters:
iter = self.resultsmodel.append(self.usersiters[user], encoded_row)
else:
iter = self.resultsmodel.append(None, encoded_row)
except Exception, e:
types=[]
for i in encoded_row:
types.append( type(i))
#print types
print "Search row error:", e, encoded_row
iter=None
path = None
if iter is not None:
path = self.resultsmodel.get_path(iter)
if path is not None:
if self.usersGroup.get_active() and self.ExpandButton.get_active():
self.ResultsList.expand_to_path(path)
returned += 1
itercounter += 1
if itercounter > self.frame.np.config.sections['searches']["max_stored_results"]:
break
self.CountVisibleResults()
return returned
def updateStatus(self, user, status):
self.Searches.users[user] = status
pos = 0
for r in self.all_data:
if user == r[1]:
self.all_data[pos][16] = status
pos += 1
iter = self.resultsmodel.get_iter_root()
while iter is not None:
selected_user = self.resultsmodel.get_value(iter, 1)
if selected_user == user:
self.resultsmodel.set_value(iter, 17, status)
if self.resultsmodel.iter_has_child(iter):
child = self.resultsmodel.iter_children(iter)
while child is not None:
selected_user = self.resultsmodel.get_value(child, 1)
if selected_user == user:
self.resultsmodel.set_value(child, 17, status)
child = self.resultsmodel.iter_next(child)
iter = self.resultsmodel.iter_next(iter)
def sort(self):
col = self.sort_col
order = self.sort_order
if col == 3:
col = 14
elif col == 4:
col = 15
elif col == 5:
col = 16
if self.COLUMN_TYPES[col] == gobject.TYPE_STRING:
compare = locale.strcoll
else:
compare = cmp
if order == gtk.SORT_ASCENDING:
#self.data.sort(lambda r1,r2: compare(r1[col], r2[col]))
self.all_data.sort(lambda r1,r2: compare(r1[col], r2[col]))
else:
#self.data.sort(lambda r2,r1: compare(r1[col], r2[col]))
self.all_data.sort(lambda r2,r1: compare(r1[col], r2[col]))
def checkDigit(self, filter, value, factorize = True):
op = ">="
if filter[:1] in (">", "<", "="):
op, filter = filter[:1]+"=", filter[1:]
if not filter:
return True
factor = 1
if factorize:
base = 1024
if filter[-1:].lower() == 'b':
filter = filter[:-1] # stripping off the b, we always assume it means bytes
if filter[-1:].lower() == 'i':
base = 1000
filter = filter[:-1]
if filter.lower()[-1:] == "g":
factor = pow(base, 3)
filter = filter[:-1]
elif filter.lower()[-1:] == "m":
factor = pow(base, 2)
filter = filter[:-1]
elif filter.lower()[-1:] == "k":
factor = base
filter = filter[:-1]
if not filter:
return True
try:
filter = long(filter) * factor
except TypeError:
return True
if eval(str(value)+op+str(filter), {}):
return True
return False
def check_filter(self, row):
filters = self.filters
if not self.filtersCheck.get_active():
return True
if filters[0] and not filters[0].search(row[2].lower()):
return False
if filters[1] and filters[1].search(row[2].lower()):
return False
if filters[2] and not self.checkDigit(filters[2], row[13]):
return False
if filters[3] and not self.checkDigit(filters[3], row[10], False):
return False
if filters[4] and row[6] != _("Y"):
return False
if filters[5]:
for cc in filters[5]:
if not cc:
continue
if row[12] is None:
return False
if cc[0] == "-":
if row[12].upper() == cc[1:].upper():
return False
elif cc.upper() != row[12].upper():
return False
return True
def set_filters(self, enable, f_in, f_out, size, bitrate, freeslot, country):
if self.frame.np.transfers is None:
encode = self.frame.np.encode
else:
encode = self.frame.np.transfers.encode
self.filters = [None, None, None, None, freeslot, None]
if f_in:
try:
f_in = re.compile(f_in.lower())
self.filters[0] = f_in
except sre_constants.error:
self.frame.SetTextBG(self.FilterIn.child, "red", "white")
else:
self.frame.SetTextBG(self.FilterIn.child)
if f_out:
try:
f_out = re.compile(f_out.lower())
self.filters[1] = f_out
except sre_constants.error:
self.frame.SetTextBG(self.FilterOut.child, "red", "white")
else:
self.frame.SetTextBG(self.FilterOut.child)
if size:
self.filters[2] = size
if bitrate:
self.filters[3] = bitrate
if country:
self.filters[5] = country.upper().split(" ")
self.usersiters.clear()
self.resultsmodel.clear()
#data = []
displaycounter = 0
for row in self.all_data:
if self.check_filter(row):
ix, user, filename, h_size, h_speed, h_queue, immediatedl, h_bitrate, length, directory, bitrate, fullpath, country, size, speed, queue, status = row
if user in self.Searches.users and status != self.Searches.users[user]:
status = self.Searches.users[user]
#user, filename, size, speed, queue, immediatedl, h_bitrate, length, directory, bitrate, fullpath, country, status
if self.usersGroup.get_active() and user not in self.usersiters:
self.usersiters[user] = self.resultsmodel.append(None, [0, user, "", "", h_speed, h_queue, immediatedl, "", "", self.get_flag(user, country), "", 0, "", country, 0, speed, queue, status])
encoded_row = [ix, user, encode(filename, user), h_size, h_speed, h_queue, immediatedl, h_bitrate, length, self.get_flag(user, country), encode(directory, user), bitrate, encode( fullpath, user), country, size, speed, queue, status]
try:
if user in self.usersiters:
iter = self.resultsmodel.append(self.usersiters[user], encoded_row)
else:
iter = self.resultsmodel.append(None, encoded_row)
except Exception, e:
print "Filters: Search row error:", e
for i in encoded_row:
print i, type(i),
print
displaycounter += 1
if displaycounter >= self.frame.np.config.sections['searches']["max_displayed_results"]:
break
self.CountVisibleResults()
def CountVisibleResults(self):
if self.usersGroup.get_active():
iter_count = 0
user_count = self.resultsmodel.iter_n_children(None)
for i in range(user_count):
iters = self.resultsmodel.iter_nth_child(None, i)
iter_count += self.resultsmodel.iter_n_children(iters)
self.Counter.set_text("Results: %d/%d" %(iter_count ,len(self.all_data)) )
else:
self.Counter.set_text("Results: %d/%d" %(self.resultsmodel.iter_n_children(None) ,len(self.all_data)) )
def OnPopupMenuUsers(self, widget):
self.select_results()
#(user, fn, size, bitrate, length)
self.popup_menu_users.clear()
if len(self.selected_users) > 0:
items = []
self.selected_users.sort(key=str.lower)
for user in self.selected_users:
popup = PopupMenu(self.frame)
popup.setup(
("#" + _("Send _message"), popup.OnSendMessage, gtk.STOCK_EDIT),
("#" + _("Show IP a_ddress"), popup.OnShowIPaddress, gtk.STOCK_NETWORK),
("#" + _("Get user i_nfo"), popup.OnGetUserInfo, gtk.STOCK_DIALOG_INFO),
("#" + _("Brow_se files"), popup.OnBrowseUser, gtk.STOCK_HARDDISK),
("#" + _("Gi_ve privileges"), popup.OnGivePrivileges, gtk.STOCK_JUMP_TO),
("", None),
("$" + _("_Add user to list"), popup.OnAddToList),
("$" + _("_Ban this user"), popup.OnBanUser),
("$" + _("_Ignore this user"), popup.OnIgnoreUser),
("#" + _("Select User's Results"), self.OnSelectUserResults, gtk.STOCK_INDEX),
)
popup.set_user(user)
items.append((1, user, popup, self.OnPopupMenuUser, popup))
self.popup_menu_users.setup(*items)
return True
def OnPopupMenuUser(self, widget, popup=None):
if popup is None:
return
menu = popup
user = menu.user
items = menu.get_children()
act = False
if len(self.selected_users) >= 1:
act = True
items[0].set_sensitive(act)
items[1].set_sensitive(act)
items[2].set_sensitive(act)
items[3].set_sensitive(act)
items[6].set_active(user in [i[0] for i in self.frame.np.config.sections["server"]["userlist"]])
items[7].set_active(user in self.frame.np.config.sections["server"]["banlist"])
items[8].set_active(user in self.frame.np.config.sections["server"]["ignorelist"])
for i in range(4, 9):
items[i].set_sensitive(act)
return True
def OnSelectUserResults(self, widget):
if len(self.selected_users) == 0:
return
selected_user = widget.parent.user
sel = self.ResultsList.get_selection()
fmodel = self.ResultsList.get_model()
sel.unselect_all()
iter = self.resultsmodel.get_iter_root()
while iter is not None:
user = self.resultsmodel.get_value(iter, 1)
if selected_user == user:
fn = self.resultsmodel.get_value(iter, 11)
if fn == "" or self.resultsmodel.iter_has_child(iter):
child = self.resultsmodel.iter_children(iter)
while child is not None:
user = self.resultsmodel.get_value(child, 1)
if selected_user == user:
sel.select_path(fmodel.get_path(child),)
child = self.resultsmodel.iter_next(child)
else:
ix = fmodel.get_path(iter)
sel.select_path(ix,)
iter = self.resultsmodel.iter_next(iter)
self.select_results()
def select_results(self):
self.selected_results = []
self.selected_users = []
self.ResultsList.get_selection().selected_foreach(self.SelectedResultsCallback)
def ChangeColours(self):
self.frame.SetTextBG(self.ResultsList)
self.frame.SetTextBG(self.FilterIn.child)
self.frame.SetTextBG(self.FilterOut.child)
self.frame.SetTextBG(self.FilterSize.child)
self.frame.SetTextBG(self.FilterBitrate.child)
self.frame.SetTextBG(self.FilterCountry.child)
self.frame.SetTextBG(self.RememberCheckButton)
self.frame.SetTextBG(self.FilterFreeSlot)
font = self.frame.np.config.sections["ui"]["searchfont"]
self.frame.ChangeListFont(self.ResultsList, font)
def GetUserStatus(self, msg):
if msg.user not in self.users:
return
self.updateStatus(msg.user, msg.status)
def NonExistantUser(self, user):
if user not in self.users:
return
self.updateStatus(user, -1)
def saveColumns(self):
columns = []
for column in self.ResultsList.get_columns():
columns.append(column.get_visible())
self.frame.np.config.sections["columns"]["search"] = columns
def SelectedResultsCallback(self, model, path, iter):
num = model.get_value(iter, 0)
user = model.get_value(iter, 1)
fn = None
for r in self.all_data:
if num != r[0] or user != r[1]:
continue
#user = r[1]
fn = r[11]
size = r[13]
bitrate = r[7]
length = r[8]
break
if user is None:
return
if not user in self.selected_users:
self.selected_users.append(user)
if fn is None or fn == "":
return
self.selected_results.append((user, fn, size, bitrate, length))
def OnListClicked(self, widget, event):
if event.button == 1 and event.type == gtk.gdk._2BUTTON_PRESS:
self.select_results()
self.OnDownloadFiles(widget)
self.ResultsList.get_selection().unselect_all();
return True
elif event.button == 3:
return self.OnPopupMenu(widget, event)
return False
def OnPopupMenu(self, widget, event):
if event.button != 3:
return False
self.select_results()
items = self.popup_menu.get_children()
users = len(self.selected_users) > 0
files = len(self.selected_results) > 0
for i in range(0, 5):
items[i].set_sensitive(files)
items[6].set_sensitive(files)
items[7].set_sensitive(files)
items[8].set_sensitive(users)
widget.emit_stop_by_name("button_press_event")
self.popup_menu.popup(None, None, None, event.button, event.time)
return True
def CellDataFunc(self, column, cellrenderer, model, iter):
status = model.get_value(iter, 17)
imdl = model.get_value(iter, 6)
color = imdl == _("Y") and "search" or "searchq"
colour = None
if status == 0:
colour = self.frame.np.config.sections["ui"]["searchoffline"]
cellrenderer.set_property("background", None)
elif status == -1:
colour = "#ffffff"
cellrenderer.set_property("background", "#ff0000")
else:
colour = self.frame.np.config.sections["ui"][color] or None
cellrenderer.set_property("background", None)
cellrenderer.set_property("foreground", colour)
def MetaBox(self, title="Meta Data", message="", data=None, modal= True):
win = MetaDialog( self.frame, message, data, modal)
win.set_title(title)
win.set_icon(self.frame.images["n"])
win.set_default_size(300, 100)
win.show()
gtk.main()
return win.ret
def SelectedResultsAllData(self, model, path, iter, data):
num = model.get_value(iter, 0)
filename = model.get_value(iter, 2)
user = model.get_value(iter, 1)
size = model.get_value(iter, 3)
speed = model.get_value(iter, 4)
queue = model.get_value(iter, 5)
immediate = model.get_value(iter, 6)
bitratestr = model.get_value(iter, 7)
length = model.get_value(iter, 8)
directory = model.get_value(iter, 10)
#bitrate = model.get_value(iter, 10)
fn = model.get_value(iter, 12)
country = model.get_value(iter, 13)
data[len(data)] = {"user":user, "fn": fn, "position":num, "filename":filename, "directory":directory, "size":size, "speed":speed, "queue":queue, "immediate":immediate, "bitrate":bitratestr, "length":length, "country":country}
def OnSearchMeta(self, widget):
if not self.frame.np.transfers:
return
data = {}
self.ResultsList.get_selection().selected_foreach(self.SelectedResultsAllData, data)
if data != {}:
self.MetaBox(title=_("Nicotine+: Search Results"), message=_("Metadata for Search Query: %s") % self.text, data=data, modal=True)
def OnDownloadFiles(self, widget, prefix = ""):
if not self.frame.np.transfers:
return
for file in self.selected_results:
self.frame.np.transfers.getFile(file[0], file[1], prefix, size=file[2], bitrate=file[3], length=file[4])
def OnDownloadFilesTo(self, widget):
subdir = None
for file in self.selected_results:
subdir = file[1].rsplit("\\", 1)[0].rsplit("\\", 1)[1]
break
dir = ChooseDir(self.frame.MainWindow, self.frame.np.config.sections["transfers"]["downloaddir"], create=True, name=subdir)
if dir is None:
return
for dirs in dir:
self.OnDownloadFiles(widget, dirs)
break
def OnDownloadFolders(self, widget):
folders = []
for i in self.selected_results:
user = i[0]
dir = string.join(i[1].split("\\")[:-1], "\\")
if (user, dir) in folders:
continue
self.frame.np.ProcessRequestToPeer(user, slskmessages.FolderContentsRequest(None, dir))
folders.append((user, dir))
if user not in self.frame.np.requestedFolders:
continue
if dir in self.frame.np.requestedFolders[user]:
del self.frame.np.requestedFolders[user][dir]
def OnDownloadFoldersTo(self, widget):
subdir = None
folders = []
directories = ChooseDir(self.frame.MainWindow, self.frame.np.config.sections["transfers"]["downloaddir"], create=True, name=subdir)
if directories is None or directories == []:
return
destination = directories[0]
for i in self.selected_results:
user = i[0]
dir = string.join(i[1].split("\\")[:-1], "\\")
if (user, dir) in folders:
continue
folders.append((user, dir))
for tup in folders:
user, dir = tup
if user not in self.frame.np.requestedFolders:
self.frame.np.requestedFolders[user] = {}
self.frame.np.requestedFolders[user][dir] = destination
self.frame.np.ProcessRequestToPeer(user, slskmessages.FolderContentsRequest(None, dir))
def OnCopyURL(self, widget):
user, path = self.selected_results[0][:2]
self.frame.SetClipboardURL(user, path)
def OnCopyDirURL(self, widget):
user, path = self.selected_results[0][:2]
path = string.join(path.split("\\")[:-1], "\\") + "\\"
if path[:-1] != "/":
path += "/"
self.frame.SetClipboardURL(user, path)
def OnGroup(self, widget):
self.OnRefilter(widget)
self.ResultsList.set_property("show-expanders", widget.get_active())
if widget.get_active():
self.ResultsList.get_columns()[0].set_visible(False)
self.ExpandButton.show()
else:
self.ResultsList.get_columns()[0].set_visible(True)
self.ExpandButton.hide()
def OnToggleExpandAll(self, widget):
if self.ExpandButton.get_active():
self.ResultsList.expand_all()
self.expandImage.set_from_stock(gtk.STOCK_REMOVE, 4)
else:
self.ResultsList.collapse_all()
self.expandImage.set_from_stock(gtk.STOCK_ADD, 4)
def OnToggleFilters(self, widget):
if widget.get_active():
self.Filters.show()
self.OnRefilter(None)
else:
self.Filters.hide()
self.ResultsList.set_model(None)
self.set_filters(0, None, None, None, None, None, "")
self.ResultsList.set_model(self.resultsmodel)
if self.usersGroup.get_active():
if self.ExpandButton.get_active():
self.ResultsList.expand_all()
else:
self.ResultsList.collapse_all()
def OnIgnore(self, widget):
try:
del self.Searches.searches[self.id]
except KeyError:
pass
self.Searches.WishListDialog.removeWish(self.text)
widget.set_sensitive(False)
def OnClear(self, widget):
self.all_data = []
self.usersiters.clear()
self.resultsmodel.clear()
def OnClose(self, widget):
if not self.frame.np.config.sections["searches"]["reopen_tabs"]:
if self.text not in self.frame.np.config.sections["server"]["autosearch"]:
self.OnIgnore(widget)
self.Searches.RemoveTab(self)
def OnToggleRemember(self, widget):
self.remember = widget.get_active()
if not self.remember:
self.Searches.RemoveAutoSearch(self.id)
else:
self.Searches.AutoSearch(self.id)
def PushHistory(self, widget, title):
text = widget.child.get_text()
if not text.strip():
return None
text = text.strip()
history = self.frame.np.config.sections["searches"][title]
self.frame.np.config.pushHistory(history, text, 5)
self.AddCombo(widget, text)
widget.child.set_text(text)
return text
def OnRefilter(self, widget):
f_in = self.PushHistory(self.FilterIn, "filterin")
f_out = self.PushHistory(self.FilterOut, "filterout")
f_size = self.PushHistory(self.FilterSize, "filtersize")
f_br = self.PushHistory(self.FilterBitrate, "filterbr")
f_free = self.FilterFreeSlot.get_active()
f_country = self.PushHistory(self.FilterCountry, "filtercc")
self.ResultsList.set_model(None)
self.set_filters(1, f_in, f_out, f_size, f_br, f_free, f_country)
self.ResultsList.set_model(self.resultsmodel)
if self.usersGroup.get_active():
if self.ExpandButton.get_active():
self.ResultsList.expand_all()
else:
self.ResultsList.collapse_all()