# gosync is an open source Google Drive(TM) sync application for Linux # # Copyright (C) 2015 Himanshu Chauhan # # 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 2 # 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, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. import wx, os, time import sys if sys.version_info > (3,): long = int try : import wx.adv wxgtk4 = True except (ImportError, ValueError): wxgtk4 = False import sys, os, wx, ntpath, threading, math, webbrowser from threading import Timer try : from .GoSyncModel import GoSyncModel, ClientSecretsNotFound from .defines import * from .DriveUsageBox import DriveUsageBox from .GoSyncEvents import * from .GoSyncSelectionPage import SelectionPage from .GoSyncSettingPage import SettingsPage except (ImportError, ValueError): from GoSyncModel import GoSyncModel, ClientSecretsNotFound from defines import * from DriveUsageBox import DriveUsageBox from GoSyncEvents import * from GoSyncSelectionPage import SelectionPage from GoSyncSettingPage import SettingsPage ID_SYNC_TOGGLE = wx.NewId() ID_SYNC_NOW = wx.NewId() ID_RECALC_USAGE = wx.NewId() mainWindowStyle = wx.DEFAULT_FRAME_STYLE & (~wx.CLOSE_BOX) & (~wx.MAXIMIZE_BOX) ^ (wx.RESIZE_BORDER) HERE=os.path.abspath(os.path.dirname(__file__)) class PageAccount(wx.Panel): def __init__(self, parent, sync_model): wx.Panel.__init__(self, parent, size=parent.GetSize(), style=wx.RAISED_BORDER) self.sync_model = sync_model self.totalFiles = 0 self.time_left=0 aboutdrive = sync_model.DriveInfo() self.driveUsageBar = DriveUsageBox(self, long(aboutdrive['storageQuota']['limit']), -1) self.driveUsageBar.SetStatusMessage("Calculating your categorical Google Drive usage. Please wait.") self.driveUsageBar.SetMoviesUsage(0) self.driveUsageBar.SetDocumentUsage(0) self.driveUsageBar.SetOthersUsage(0) self.driveUsageBar.SetAudioUsage(0) self.driveUsageBar.SetPhotoUsage(0) self.driveUsageBar.RePaint() mainsizer = wx.BoxSizer(wx.VERTICAL) mainsizer.AddSpacer(10) self.SetSizerAndFit(mainsizer) GoSyncEventController().BindEvent(self, GOSYNC_EVENT_CALCULATE_USAGE_STARTED, self.OnUsageCalculationStarted) GoSyncEventController().BindEvent(self, GOSYNC_EVENT_CALCULATE_USAGE_DONE, self.OnUsageCalculationDone) GoSyncEventController().BindEvent(self, GOSYNC_EVENT_CALCULATE_USAGE_UPDATE, self.OnUsageCalculationUpdate) def OnUsageCalculationDone(self, event): if not event.data: self.driveUsageBar.SetStatusMessage("Your Google Drive usage is shown below:") self.driveUsageBar.SetMoviesUsage(self.sync_model.GetMovieUsage()) self.driveUsageBar.SetDocumentUsage(self.sync_model.GetDocumentUsage()) self.driveUsageBar.SetOthersUsage(self.sync_model.GetOthersUsage()) self.driveUsageBar.SetAudioUsage(self.sync_model.GetAudioUsage()) self.driveUsageBar.SetPhotoUsage(self.sync_model.GetPhotoUsage()) self.driveUsageBar.RePaint() else: self.driveUsageBar.SetStatusMessage("Sorry, could not calculate your Google Drive usage.") def OnUsageCalculationUpdate(self, event): self.totalFiles = event.data self.driveUsageBar.SetStatusMessage("Calculating usage. Files Scanned: %d\n" % self.totalFiles) self.driveUsageBar.SetMoviesUsage(self.sync_model.GetMovieUsage()) self.driveUsageBar.SetDocumentUsage(self.sync_model.GetDocumentUsage()) self.driveUsageBar.SetOthersUsage(self.sync_model.GetOthersUsage()) self.driveUsageBar.SetAudioUsage(self.sync_model.GetAudioUsage()) self.driveUsageBar.SetPhotoUsage(self.sync_model.GetPhotoUsage()) self.driveUsageBar.RePaint() def OnUsageCalculationStarted(self, event): self.totalFiles = event.data self.driveUsageBar.SetStatusMessage("Calculating your categorical Google Drive usage. Please wait.") class GoSyncController(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, title="GoSync", size=(490,500), style=mainWindowStyle) try: self.sync_model = GoSyncModel() except ClientSecretsNotFound: dial = wx.MessageDialog(None, 'Credentials file was not found!\n\nDo you want to know how to create one?\n', 'Error', wx.YES_NO | wx.ICON_EXCLAMATION) res = dial.ShowModal() if res == wx.ID_YES: webbrowser.open(CLIENT_SECRET_HELP_SITE, new=1, autoraise=True) sys.exit(1) except: dial = wx.MessageDialog(None, 'GoSync failed to initialize\n', 'Error', wx.OK | wx.ICON_EXCLAMATION) res = dial.ShowModal() sys.exit(1) self.aboutdrive = self.sync_model.DriveInfo() self.Bind(wx.EVT_CLOSE, self.OnExit) title_string = "GoSync - %s (%s used of %s)" % (self.aboutdrive['user']['displayName'], self.FileSizeHumanize(long(self.aboutdrive['storageQuota']['usageInDrive'])), self.FileSizeHumanize(long(self.aboutdrive['storageQuota']['limit']))) self.SetTitle(title_string) appIcon = wx.Icon(APP_ICON, wx.BITMAP_TYPE_PNG) self.SetIcon(appIcon) menuBar = wx.MenuBar() menu = wx.Menu() menu_txt = 'Pause/Resume Sync' self.pr_item = self.CreateMenuItem(menu, menu_txt, self.OnToggleSync, icon=os.path.join(HERE, 'resources/sync-menu.png'), id=ID_SYNC_TOGGLE) self.sync_now_mitem = self.CreateMenuItem(menu, 'Sync Now!', self.OnSyncNow, icon=os.path.join(HERE, 'resources/sync-menu.png'), id=ID_SYNC_NOW) self.rcu = self.CreateMenuItem(menu, 'Recalculate Drive Usage', self.OnRecalculateDriveUsage, icon=os.path.join(HERE, 'resources/sync-menu.png'), id=ID_RECALC_USAGE) menu.AppendSeparator() self.CreateMenuItem(menu, 'A&bout', self.OnAbout, os.path.join(HERE, 'resources/info.png')) self.CreateMenuItem(menu, 'E&xit', self.OnExit, os.path.join(HERE, 'resources/exit.png')) menuBar.Append(menu, '&File') self.SetMenuBar(menuBar) # Here we create a panel and a notebook on the panel p = wx.Panel(self, size=self.GetSize()) nb = wx.Notebook(p) # create the page windows as children of the notebook accountPage = PageAccount(nb, self.sync_model) selectionPage = SelectionPage(nb, self.sync_model) settingPage = SettingsPage(nb, self.sync_model) # add the pages to the notebook with the label to show on the tab nb.AddPage(accountPage, "Account") nb.AddPage(selectionPage, "What to sync?") nb.AddPage(settingPage, "Settings") # finally, put the notebook in a sizer for the panel to manage # the layout sizer = wx.BoxSizer() sizer.Add(nb, 1, wx.EXPAND) p.SetSizer(sizer) self.sb = self.CreateStatusBar(2) self.sb.SetStatusWidths([-6, -1]) if self.sync_model.IsSyncEnabled(): self.sb.SetStatusText("Running", 1) self.pr_item.SetItemLabel("Pause Sync") else: self.sb.SetStatusText("") self.sb.SetStatusText("Paused", 1) self.pr_item.SetItemLabel("Resume Sync") GoSyncEventController().BindEvent(self, GOSYNC_EVENT_SYNC_STARTED, self.OnSyncStarted) GoSyncEventController().BindEvent(self, GOSYNC_EVENT_SYNC_UPDATE, self.OnSyncUpdate) GoSyncEventController().BindEvent(self, GOSYNC_EVENT_SYNC_DONE, self.OnSyncDone) GoSyncEventController().BindEvent(self, GOSYNC_EVENT_SYNC_TIMER, self.OnSyncTimer) GoSyncEventController().BindEvent(self, GOSYNC_EVENT_INTERNET_UNREACHABLE, self.OnInternetDown) GoSyncEventController().BindEvent(self, GOSYNC_EVENT_SYNC_INV_FOLDER, self.OnSyncInvalidFolder) GoSyncEventController().BindEvent(self, GOSYNC_EVENT_SCAN_UPDATE, self.OnScanUpdate) GoSyncEventController().BindEvent(self, GOSYNC_EVENT_CALCULATE_USAGE_DONE, self.OnUsageCalculationDone) GoSyncEventController().BindEvent(self, GOSYNC_EVENT_CALCULATE_USAGE_STARTED, self.OnUsageCalculationStarted) self.sync_model.SetTheBallRolling() def OnUsageCalculationStarted(self, event): self.pr_item.Enable(False) self.sync_now_mitem.Enable(False) self.rcu.Enable(False) def OnUsageCalculationDone(self, event): self.pr_item.Enable(True) if self.sync_model.IsSyncEnabled(): self.sync_now_mitem.Enable(True) else: self.sync_now_mitem.Enable(False) self.rcu.Enable(True) self.sb.SetStatusText("Usage calculation completed.") def OnInternetDown(self, event): if event.data == 1: self.sb.SetStatusText("Network is down") if self.sync_model.GetUseSystemNotifSetting(): if wxgtk4: nmsg = wx.adv.NotificationMessage(title="GoSync", message="Network has gone down!") nmsg.SetFlags(wx.ICON_WARNING) nmsg.Show(timeout=wx.adv.NotificationMessage.Timeout_Auto) else: nmsg = wx.NotificationMessage("GoSync", "Network has gone down!") nmsg.SetFlags(wx.ICON_WARNING) nmsg.Show(timeout=wx.NotificationMessage.Timeout_Auto) else: self.sb.SetStatusText("Network is up!") if self.sync_model.GetUseSystemNotifSetting(): if wxgtk4: nmsg = wx.adv.NotificationMessage(title="GoSync", message="Network is up!") nmsg.SetFlags(wx.ICON_INFORMATION) nmsg.Show(timeout=wx.adv.NotificationMessage.Timeout_Auto) else: nmsg = wx.NotificationMessage("GoSync", "Network is up!") nmsg.SetFlags(wx.ICON_INFORMATION) nmsg.Show(timeout=wx.NotificationMessage.Timeout_Auto) def OnSyncInvalidFolder(self, event): if self.sync_model.GetUseSystemNotifSetting(): if wxgtk4: nmsg = wx.adv.NotificationMessage(title="GoSync", message="Invalid sync settings detected.\nPlease check the logs.") nmsg.SetFlags(wx.ICON_ERROR) nmsg.Show(timeout=wx.adv.NotificationMessage.Timeout_Auto) else: nmsg = wx.NotificationMessage("GoSync", "Invalid sync settings detected.\nPlease check the logs.") nmsg.SetFlags(wx.ICON_ERROR) nmsg.Show(timeout=wx.NotificationMessage.Timeout_Auto) def OnRecalculateDriveUsage(self, event): if self.sync_model.IsCalculatingDriveUsage() == True: dial = wx.MessageDialog(None, 'GoSync is already scaningfiles on drive.', 'In Progress', wx.OK | wx.ICON_WARNING) res = dial.ShowModal() dial.Destroy() return elif self.sync_model.IsSyncRunning() == True: dial = wx.MessageDialog(None, 'GoSync is currently syncing files from the Drive.\nThe usage will be refreshed later.', 'In Progress', wx.OK | wx.ICON_WARNING) res = dial.ShowModal() dial.Destroy() self.sync_model.ForceDriveUsageCalculation() def OnSyncTimer(self, event): unicode_string = event.data.pop() self.sb.SetStatusText(unicode_string.encode('ascii', 'ignore')) def OnSyncStarted(self, event): if self.sync_model.GetUseSystemNotifSetting(): if wxgtk4 : nmsg = wx.adv.NotificationMessage(title="GoSync", message="Sync Started") nmsg.SetFlags(wx.ICON_INFORMATION) nmsg.Show(timeout=wx.adv.NotificationMessage.Timeout_Auto) else: nmsg = wx.NotificationMessage("GoSync", "Sync Started") nmsg.SetFlags(wx.ICON_INFORMATION) nmsg.Show(timeout=wx.NotificationMessage.Timeout_Auto) self.sb.SetStatusText("Sync started...") self.sb.SetStatusText("Running", 1) self.sync_now_mitem.Enable(False) self.rcu.Enable(False) self.pr_item.SetItemLabel("Pause Sync") def OnSyncUpdate(self, event): unicode_string = event.data.pop() self.sb.SetStatusText(unicode_string.encode('ascii', 'ignore')) def OnSyncDone(self, event): if not event.data: if self.sync_model.GetUseSystemNotifSetting(): if wxgtk4: nmsg = wx.adv.NotificationMessage(title="GoSync", message="Sync Completed!") nmsg.SetFlags(wx.ICON_INFORMATION) nmsg.Show(timeout=wx.adv.NotificationMessage.Timeout_Auto) else: nmsg = wx.NotificationMessage("GoSync", "Sync Completed!") nmsg.SetFlags(wx.ICON_INFORMATION) nmsg.Show(timeout=wx.NotificationMessage.Timeout_Auto) self.sb.SetStatusText("Sync completed.") else: if self.sync_model.GetUseSystemNotifSetting(): if wxgtk4: nmsg = wx.adv.NotificationMessage(title="GoSync", message="Sync Completed with errors!\nPlease check ~/GoSync.log") nmsg.SetFlags(wx.ICON_ERROR) nmsg.Show(timeout=wx.adv.NotificationMessage.Timeout_Auto) else: nmsg = wx.NotificationMessage("GoSync", "Sync Completed with errors!\nPlease check ~/GoSync.log") nmsg.SetFlags(wx.ICON_ERROR) nmsg.Show(timeout=wx.NotificationMessage.Timeout_Auto) self.sb.SetStatusText("Sync failed. Please check the logs.") self.sync_now_mitem.Enable(True) self.rcu.Enable(True) def OnScanUpdate(self, event): unicode_string = event.data.pop() self.sb.SetStatusText(unicode_string.encode('ascii', 'ignore')) def CreateMenuItem(self, menu, label, func, icon=None, id=None): if id: item = wx.MenuItem(menu, id, label) else: item = wx.MenuItem(menu, -1, label) if icon: item.SetBitmap(wx.Bitmap(icon)) if id: self.Bind(wx.EVT_MENU, func, id=id) else: self.Bind(wx.EVT_MENU, func, id=item.GetId()) if wxgtk4 : menu.Append(item) else: menu.AppendItem(item) return item def FileSizeHumanize(self, size): size = abs(size) if (size==0): return "0B" units = ['B','KB','MB','GB','TB','PB','EB','ZB','YB'] p = math.floor(math.log(size, 2)/10) return "%.3f%s" % (size/math.pow(1024,p),units[long(p)]) def OnExit(self, event): dial = wx.MessageDialog(None, 'GoSync will stop syncing files until restarted.\nAre you sure to quit?\n', 'Question', wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION) res = dial.ShowModal() if res == wx.ID_YES: if self.sync_model.IsSyncEnabled() or self.sync_model.IsSyncRunning(): self.sync_model.StopTheShow() self.sb.SetStatusText("Paused", 1) wx.CallAfter(self.Destroy) def OnToggleSync(self, evt): if self.sync_model.IsSyncEnabled(): self.sync_model.StopSync() self.sb.SetStatusText("Sync is paused") self.sb.SetStatusText("Paused", 1) self.pr_item.SetItemLabel("Resume Sync") self.sync_now_mitem.Enable(False) else: self.sync_model.StartSync() self.sb.SetStatusText("Running", 1) self.pr_item.SetItemLabel("Pause Sync") self.sync_now_mitem.Enable(True) def OnSyncNow(self, evt): self.sync_model.time_left=1 def OnAbout(self, evt): """About GoSync""" if wxgtk4 : about = wx.adv.AboutDialogInfo() else: about = wx.AboutDialogInfo() about.SetIcon(wx.Icon(ABOUT_ICON, wx.BITMAP_TYPE_PNG)) about.SetName(APP_NAME) about.SetVersion(APP_VERSION) about.SetDescription(APP_DESCRIPTION) about.SetCopyright(APP_COPYRIGHT) about.SetWebSite(APP_WEBSITE) about.SetLicense(APP_LICENSE) about.AddDeveloper(APP_DEVELOPER) about.AddArtist(ART_DEVELOPER) if wxgtk4 : wx.adv.AboutBox(about) else: wx.AboutBox(about)