This file is indexed.

/usr/share/netio230a-gui/netio230a_gui.py is in netio230a-gui 1.0.1-3.

This file is owned by root:root, with mode 0o755.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
#!/usr/bin/env python
# -*- encoding: UTF8 -*-

# author: Philipp Klaus, philipp.l.klaus AT web.de


#   This file is part of netio230a.
#
#   netio230a 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.
#
#   netio230a 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 netio230a.  If not, see <http://www.gnu.org/licenses/>.


# documentation on PyGTK:
# http://library.gnome.org/devel/pygtk/stable/
# http://library.gnome.org/devel/pygobject/stable/
#
# good pygtk tutorial: <http://zetcode.com/tutorials/pygtktutorial/>

import sys
import os # for os.path.abspath() and os.path.dirname()
import gtk
## for debugging (set debug mark with pdb.set_trace() )
import pdb
import netio230a
# to store and retrieve recent connections:
import configuration

import gobject # for the timer
import signal # for [Ctrl]-[c] catching

PROGRAM_ICON = 'netio230a_icon.png'
DEVICE_CONTROLLER_UI = "netio230aGUI.glade"
CONNECTION_DETAIL_UI = "netio230aGUI_dialog.glade"

POSSIBLE_RESOURCE_PATHS = [ os.path.abspath(os.path.dirname(sys.argv[0])) + '/resources/', # path relative to this script
        '/usr/share/netio230a-gui/', # debian resource path
    ]

AUTO_UPDATE = 3 # auto update time seconds

OVERWRITE_TELNET_SOCKET_TIMEOUT = 1


DEBUG_MODE = True
MIN_DEBUG_LEVEL = 7


# constants do not touch:
DBG_WARNING = 8


# remember position of window:
#    (x, y) = w.get_position()
#    (w, h) = w.get_size()
#
#restore position:
#    w = gtk.Window()
#    w.move(x, y)
#    w.resize(w, h)


def getAbsoluteFilepath(filename):
    for path in POSSIBLE_RESOURCE_PATHS:
        if os.path.isfile(path + filename):
            return path + filename
    raise NameError('File ('+ filename +') not found in possible resource folders: '+str(POSSIBLE_RESOURCE_PATHS))

class AboutDialog:
    def __init__(self):
        
        self.builder = gtk.Builder()
        self.builder.add_from_file(getAbsoluteFilepath(DEVICE_CONTROLLER_UI))
        
        self.about_dialog = self.builder.get_object( "aboutDialog" )
        self.about_dialog.set_icon_from_file(getAbsoluteFilepath(PROGRAM_ICON))
    
    def run(self):
        self.about_dialog.run()
        self.about_dialog.destroy()

class DeviceController:
    def __init__(self,controller,connection_details):
        self.controller = controller
    
        self.__host = connection_details['host']
        self.__tcp_port = connection_details['tcp_port']
        self.__username = connection_details['username']
        self.__pw = connection_details['password']
        self.__persistent_connection = connection_details['persistent_network']
        try:
            self.netio = netio230a.netio230a(self.__host, self.__username, self.__pw, True, self.__tcp_port)
            self.netio.enable_logging(open(configuration.LOG_FILE,'w'))
        except StandardError, error:
            print(str(error))
        
        self.builder = gtk.Builder()
        self.builder.add_from_file(getAbsoluteFilepath(DEVICE_CONTROLLER_UI))
        
        self.window = self.builder.get_object("mainWindow")
        self.window.set_icon_from_file(getAbsoluteFilepath(PROGRAM_ICON))
        self.builder.get_object("link_button").set_uri('http://'+self.__host)
        
        self.updateLabels()
        self.updatePowerSocketStatus()
        self.builder.connect_signals(self)
        self.window.connect("window-state-event",self.handle_window_state_events)
        self.window.show()
        
        # a timer to update the UI automatically
        self.timer_id = gobject.timeout_add(1000, self.timer_tick) # use gobject.timeout_add_seconds() for longer periods
        self.timer_continue = True
        self.counter = 0
    
    
    def timer_tick(self):
        if self.timer_id is not None and self.timer_continue:
            self.counter += 1
            if self.counter%AUTO_UPDATE == 0:
                try:
                    self.updatePowerSocketStatus()
                except StandardError, error:
                    pass
                    debug("The updatePowerSocketStatus action triggered by the timer failed: " + str(error), DBG_WARNING)
                self.counter = 0
            return True # run again in one second
        return False # stop running again
    
    def handle_window_state_events(self, window, event):
        if event.changed_mask & gtk.gdk.WINDOW_STATE_ICONIFIED:
            if event.new_window_state & gtk.gdk.WINDOW_STATE_ICONIFIED:
                #print 'Window was minimized!'
                self.controller.toggle_visibility()
            #else:
            #    print 'Window was unminimized!'
    
    def cb_disconnect(self, button, *args):
        self.controller.setNextStep("runDeviceSelector")
        gtk.main_quit()
        self.window.hide()
        return False
    
    def gtk_main_quit( self, window ):
        gtk.main_quit()
    
    def on_window_destroy(self, widget, data=None):
        gtk.main_quit()
    
    def cb_about(self, button):
        about = AboutDialog()
        about.run()
        
    def cb_updateDisplay(self, notebook, page, page_num):
        self.updateStatusBar()
        if page_num == 0:
            self.updatePowerSocketStatus()
        elif page_num == 1:
            self.updateSystemSetup()
            pass
        elif page_num == 2:
            self.updatePowerSocketStatus()
        else:
            return
    
    def cb_refresh(self, button):
        self.updatePowerSocketStatus()

    def updatePowerSocketStatus(self):
        try:
            power_sockets = self.netio.getAllPowerSockets()
        except StandardError, error:
            print(str(error))
            return
        if not self.__persistent_connection:
            self.netio.disconnect()
        
        # update checkboxes on this GUI and on the status icon:
        i = 1
        new_status = []
        for power_socket in power_sockets:
            ## shorter form with builder.get_object(). cf. <http://stackoverflow.com/questions/2072976/access-to-widget-in-gtk>
            self.builder.get_object("socket"+str(i)).set_active(power_socket.getPowerOn())
            new_status.append([power_socket.getName(),power_socket.getPowerOn()])
            i += 1
        self.controller.icon.update_checkboxes(new_status)
        
        # update the status text:
        tb = gtk.TextBuffer()
        tb.set_text("power status:\nsocket 1: %s\nsocket 2: %s\nsocket 3: %s\nsocket 4: %s" % (power_sockets[0].getPowerOn(),power_sockets[1].getPowerOn(),power_sockets[2].getPowerOn(),power_sockets[3].getPowerOn()))
        self.builder.get_object("status_output").set_buffer( tb )
        self.updateStatusBar()

    def updateStatusBar(self):
        self.builder.get_object("status_label").set_text(u"ΓΈ %.1f ms/request (%d total)" % (self.netio.mean_request_time*1000, self.netio.number_of_sent_requests))
    
    def updateLabels(self):
        try:
            power_sockets = self.netio.getAllPowerSockets()
        except StandardError, error:
            print(str(error))
            return
        if not self.__persistent_connection:
            self.netio.disconnect()
        for i in range(4):
            label_name = "socket"+str(i+1)+"_label"
            self.builder.get_object(label_name).set_text(self.builder.get_object(label_name).get_text()+' ("'+power_sockets[i].getName()+'")')
    
    
    
    def updateSystemSetup(self):
        try:
            deviceAlias = self.netio.getDeviceAlias()
            version = self.netio.getFirmwareVersion()
            systemTime = self.netio.getSystemTime().isoformat(" ")
            timezoneOffset = self.netio.getSystemTimezone()
            sntpSettings = self.netio.getSntpSettings()
        except StandardError, error:
            print(str(error))
            return
        if not self.__persistent_connection:
            self.netio.disconnect()
        self.builder.get_object("device_name").set_text( deviceAlias )
        self.builder.get_object("firmware_version").set_text( version )
        self.builder.get_object("system_time").set_text( systemTime )
        self.builder.get_object("timezone_offset").set_text( str(timezoneOffset) + " hours" )
        self.builder.get_object("sntp_settings").set_text( sntpSettings )
    
        
    def cb_switch1On(self, togglebutton):
        self.__setPowerSocket(1,togglebutton.get_active())
    
    def cb_switch2On(self, togglebutton):
        self.__setPowerSocket(2,togglebutton.get_active())
    
    def cb_switch3On(self, togglebutton):
        self.__setPowerSocket(3,togglebutton.get_active())
    
    def cb_switch4On(self, togglebutton):
        self.__setPowerSocket(4,togglebutton.get_active())
    
    def __setPowerSocket(self,socket_nr,socket_power=True):
        try:
            self.netio.setPowerSocketPower(socket_nr,socket_power)
        except StandardError, error:
            print(str(error))
        if not self.__persistent_connection:
            self.netio.disconnect()
        self.updatePowerSocketStatus()

class ConnectionDetailDialog:
    def __init__(self,host='',username='admin',password='',port=1234, store_connection = True, store_password = False):
        self.builder = gtk.Builder()
        self.builder.add_from_file(getAbsoluteFilepath(CONNECTION_DETAIL_UI))
        self.dialog = self.builder.get_object("ConnectionDetailDialog")
        self.dialog.set_title("Provide connection details...")
        self.dialog.set_icon_from_file(getAbsoluteFilepath(PROGRAM_ICON))
        # pre-fill values of text entries
        self.builder.get_object("host_text").set_text(host)
        self.builder.get_object("port_text").set_text(str(port))
        self.builder.get_object("username_text").set_text(username)
        self.builder.get_object("password_text").set_text(password)
        # focus the first empty text entry:
        entry_field_names = ['host','port','username','password']
        for field_name in entry_field_names:
            if str(locals()[field_name]) == '': # this is nice trick to call the variable with the name stored in field_name
                self.builder.get_object(field_name+"_text").grab_focus()
                break
        self.builder.get_object("store_connection").set_active(store_connection)
        self.builder.get_object("store_password").set_active(store_password)
        self.builder.get_object("action_area").set_focus_chain([self.builder.get_object("connect_button"), self.builder.get_object("abort_button")])
    
    def run(self):
        self.builder.connect_signals(self)
        self.builder.get_object("store_connection").connect("toggled", self.sensitivityUpdate)
        return self.dialog.run()
    
    def sensitivityUpdate(self, widget):
        self.builder.get_object("store_password").set_sensitive(self.builder.get_object("store_connection").get_active())
    
    def updateData(self):
        self.__host = self.builder.get_object("host_text").get_text()
        self.__username = self.builder.get_object("username_text").get_text()
        self.__pw = self.builder.get_object("password_text").get_text()
        try:
            self.__tcp_port = int(self.builder.get_object("port_text").get_text())
        except:
            self.__tcp_port = 0
            self.builder.get_object("port_text").set_text("0")
        self.__store_connection = self.builder.get_object("store_connection").get_active()
        self.__store_password = self.builder.get_object("store_password").get_active()
        self.__persistent_network = self.builder.get_object("persistent_network").get_active()
    
    def enter_pressed(self, widget):
        self.builder.get_object("connect_button").activate()
        ## could also be done by setting the default response id:
        #self.dialog.set_default_response(response_id) # resp_id might be 1
    
    def response_handler(self, widget, response_id):
        self.updateData()
    
    def getData(self):
        data = dict()
        data['host'] = self.__host
        data['username'] = self.__username
        data['password'] = self.__pw
        data['tcp_port'] = self.__tcp_port
        data['store_connection'] = self.__store_connection
        data['store_password'] = self.__store_password
        data['persistent_network'] = self.__persistent_network
        return data
        

class DeviceSelector:
    # close the window and quit
    def delete_event(self, widget, event, data=None):
        gtk.main_quit()

    def __init__(self, controller):
        self.controller = controller
        # Create a new window
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.set_title("Select a Device")
        self.window.set_icon_from_file(getAbsoluteFilepath(PROGRAM_ICON))

        self.window.set_size_request(470, 220)
        self.window.connect("delete_event", self.delete_event)

        # create a TreeStore with two string columns to use as the model
        self.treestore = gtk.TreeStore(str,str,str,str,str)

        devices = netio230a.get_all_detected_devices()
        if len(devices) > 0:
            self.auto_iter = self.treestore.append(None,['auto-detected devices','','','',''])
        else:
            if os.name == 'nt':
                self.treestore.append(None,['auto-detection is not working on Windows','','','',''])
            else:
                self.treestore.append(None,['no auto-detected devices','','','',''])
        for device in devices:
            #   device name, IP, port, user, password
            self.treestore.append(self.auto_iter,[device[0],str(device[1][0])+'.'+str(device[1][1])+'.'+str(device[1][2])+'.'+str(device[1][3]),'','',''])
        
        # devices from configuration has the form [devicename, host, port, username, password]
        devices = configuration.getConfiguration()
        if len(devices) > 0:
            self.recently_iter = self.treestore.append(None,['previously used devices','','','',''])
        for device in devices:
            #   device name, IP, port, user, password
            self.treestore.append(self.recently_iter,[device[0],device[1],str(device[2]),device[3],device[4]])
        
        # more on TreeViews: <http://www.thesatya.com/blog/2007/10/pygtk_treeview.html>
        # and <http://www.pygtk.org/pygtk2tutorial/ch-TreeViewWidget.html#sec-TreeViewOverview>
        # create the TreeView using treestore
        self.treeview = gtk.TreeView(self.treestore)
        # create the TreeViewColumn to display the data
        self.tvc_device_name = gtk.TreeViewColumn('Device Name')
        self.tvc_ip = gtk.TreeViewColumn('IP Address')
        self.tvc_tcp_port = gtk.TreeViewColumn('TCP Port')
        self.tvc_user_name = gtk.TreeViewColumn('User Name')
        # set alignment of the column titles to right
        #self.tvc_ip.set_alignment(1.0)
        #self.tvc_tcp_port.set_alignment(1.0)
        # add tvcolumn to treeview
        self.treeview.append_column(self.tvc_device_name)
        self.treeview.append_column(self.tvc_ip)
        self.treeview.append_column(self.tvc_tcp_port)
        self.treeview.append_column(self.tvc_user_name)
        # create a CellRendererText to render the data
        self.cell = gtk.CellRendererText()
        self.cell_right_align = gtk.CellRendererText()
        self.cell_right_align.set_property('xalign', 1.0)
        # add the cell to the tvcolumn and allow it to expand
        self.tvc_device_name.pack_start(self.cell, True)
        self.tvc_ip.pack_start(self.cell_right_align, True)
        self.tvc_tcp_port.pack_start(self.cell_right_align, True)
        self.tvc_user_name.pack_start(self.cell, True)
        # set the cell "text" attribute to column 0 - retrieve text from that column in treestore
        self.tvc_device_name.add_attribute(self.cell, 'text', 0)
        self.tvc_ip.add_attribute(self.cell_right_align, 'text', 1)
        self.tvc_tcp_port.add_attribute(self.cell_right_align, 'text', 2)
        self.tvc_user_name.add_attribute(self.cell, 'text', 3)
        # make it searchable
        self.treeview.set_search_column(0)
        # Allow sorting on the column
        self.tvc_device_name.set_sort_column_id(0)
        self.tvc_ip.set_sort_column_id(1)
        self.tvc_tcp_port.set_sort_column_id(2)
        self.tvc_user_name.set_sort_column_id(3)
        # Allow drag and drop reordering of rows
        self.treeview.set_reorderable(True)
        self.treeview.expand_all()
        self.treeview.set_size_request(-1,200)

        spacing, homogeneous, expand, fill, padding = 2, False, True, True, 2
        # Create a new hbox with the appropriate homogeneous
        # and spacing settings
        box = gtk.HBox(homogeneous, spacing)
        
        # create the buttons
        button = gtk.Button("other device")
        box.pack_start(button, expand, fill, padding)
        button.connect("clicked",self.connect_clicked)
        button = gtk.Button("connect")
        box.pack_start(button, expand, fill, padding)
        button.connect("clicked",self.connect_clicked, self.treeview)
        
        scroll = gtk.ScrolledWindow()
        scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC);
        scroll.add(self.treeview);

        spacing, homogeneous, expand, fill, padding = 1, False, False, True, 2
        superbox = gtk.VBox(homogeneous, spacing)
        superbox.pack_start(scroll, True, True, 1)
        superbox.pack_start(box, False, False, 0)
        
        self.superbox = superbox
        
        self.window.add(self.superbox)
        self.window.show_all()
        
    def connect_clicked(self, button, *args):
        host = ''
        stored_connection = False
        for arg in args:
            if type(arg)==gtk.TreeView:
                (model, treeiter) = arg.get_selection().get_selected()
                host = model.get_value(treeiter,1)
                parent_iter = model.iter_parent(treeiter)
                # compare the text (of the 1st col) of the parent node with the text of the recently_iter node
                try:
                    if model.get_value(self.recently_iter,0) == model.get_value(parent_iter,0):
                        stored_connection = True
                        tcp_port = model.get_value(treeiter,2)
                        username = model.get_value(treeiter,3)
                        password = model.get_value(treeiter,4)
                        store_password = False if password=='' else True
                except:
                    # we don't have recently used devices yet...
                    pass
                if host == '':
                    return
                #dlg = gtk.Dialog(title='Ein Dialog',
                #    parent=self.window,
                #    buttons=(gtk.STOCK_CANCEL,
                #             gtk.RESPONSE_REJECT,
                #             gtk.STOCK_OK,
                #             gtk.RESPONSE_OK))
                #result = dlg.run()
                #if result == gtk.RESPONSE_OK:
                #    print 'Mach mal!'
                #else:
                #    print 'Lieber nicht.'
                #dlg.destroy()
        if stored_connection:
            self.dl = ConnectionDetailDialog(host, username, password, tcp_port, stored_connection, store_password)
        else:
            self.dl = ConnectionDetailDialog(host)
        self.controller.deny_quit = True
        result = self.dl.run()
        self.controller.deny_quit = False
        while result == 1:
            data = self.dl.getData()
            try:
                netio = netio230a.netio230a(data['host'], data['username'], data['password'], True, data['tcp_port'])
                devicename = netio.getDeviceAlias()
                netio = None
                break
            except StandardError, error:
                print(str(error))
                netio = None
                continue_abort = gtk.MessageDialog(parent=self.dl.dialog, flags=gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT, type=gtk.MESSAGE_INFO, buttons=gtk.BUTTONS_OK_CANCEL, message_format="Connection failed. \n\n"+str(error)+"\n\nChange connection details and try again?")
                response = continue_abort.run()
                continue_abort.destroy()
                if response == gtk.RESPONSE_OK:
                    self.controller.deny_quit = True
                    result = self.dl.run()
                    self.controller.deny_quit = False
                else:
                    result = 0
                    break
        
        self.dl.dialog.hide()
        del self.dl
        if result != 1:
            return
        
        # connection successful, do want to store the configuration?
        if data['store_connection'] == True:
            configuration.changeConfiguration(configuration.UPDATE, devicename, data['host'], data['tcp_port'], data['username'], data['password'] if data['store_password'] else '')
        else:
            configuration.changeConfiguration(configuration.REMOVE, devicename, data['host'], data['tcp_port'], data['username'], '')
            md = gtk.MessageDialog(self.window, gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_INFO, gtk.BUTTONS_CLOSE, "Connection details removed from configuration file.")
            md.run()
            md.destroy()
        self.controller.setNextStep("runDeviceController", host = data['host'], tcp_port = data['tcp_port'], username=data['username'], password = data['password'], persistent_network = data['persistent_network'])
        #self.window.hide()
        self.window.destroy()
        gtk.main_quit()
        return False

class TrayIcon(gtk.StatusIcon):
    # reely adapted from the tracker-applet: <https://labs.codethink.co.uk/index.php/p/tracker/source/tree/master/python/applet/applet.py>
    # one more resource:
    # please note that the context menu (just as any other menu in Gnome will not have icons unless you set the gconf key /desktop/gnome/interface/menus_have_icons to true. For further information see <https://bugzilla.gnome.org/show_bug.cgi?id=557469>.
    def __init__(self,controller):
        gtk.StatusIcon.__init__(self)
        self.block_changes = True
        self.controller = controller
        self.set_disconnected_ui()
        self.set_from_file(getAbsoluteFilepath(PROGRAM_ICON))
        self.set_tooltip('NETIO-230A control')
        self.set_visible(True)
        self.connect('activate', self.on_activate)
        self.connect('popup-menu', self.on_popup_menu)
        self.block_changes = False
    
    def set_disconnected_ui(self):
        self.connected_mode = False
        menu = '''
            <ui>
             <menubar name="Menubar">
              <menu action="Menu">
               <menuitem action="ConnectNote"/>
               <separator/>
               <menuitem action="About"/>
               <separator/>
               <menuitem action="Quit"/>
              </menu>
             </menubar>
            </ui>
        '''
        # order of the elements in the action tuples:
        # The name of the action. Must be specified.
        # The stock id for the action. Optional with a default value of None if a label is specified.
        # The label for the action. This field should typically be marked for translation, see the set_translation_domain() method. Optional with a default value of None if a stock id is specified.
        # The accelerator for the action, in the format understood by the gtk.accelerator_parse() function. Optional with a default value of None.
        # The tooltip for the action. This field should typically be marked for translation, see the set_translation_domain() method. Optional with a default value of None.
        # The callback function invoked when the action is activated. Optional with a default value of None.
        actions = [
            ('Menu',  None, 'Menu'),
            #('Search', None, '_Search...', None, 'Search files with MetaTracker', self.on_activate),
            ('ConnectNote', None, ' - please connect first... - ', None, 'Please connect to a NETIO-230A device to be able to power on/off sockets.', self.on_toggle),
            ('About', gtk.STOCK_ABOUT, '_About...', None, 'About NETIO-230A control', self.on_about),
            ('Quit', gtk.STOCK_QUIT, '_Quit', None, 'Quit the program', self.quit),]
        ag = gtk.ActionGroup('Actions')
        ag.add_actions(actions)
        self.manager = gtk.UIManager()
        self.manager.insert_action_group(ag, 0)
        self.manager.add_ui_from_string(menu)
        self.menu = self.manager.get_widget('/Menubar/Menu/About').props.parent
        connect_note = self.manager.get_widget('/Menubar/Menu/ConnectNote')
        image = gtk.Image()
        image.set_from_file(getAbsoluteFilepath(PROGRAM_ICON))
        connect_note.set_image(image)
        #search.get_children()[0].set_markup('<b>_Search...</b>')
        #search.get_children()[0].set_use_underline(True)
        #search.get_children()[0].set_use_markup(True)
        #search.get_children()[1].set_from_stock(gtk.STOCK_FIND, gtk.ICON_SIZE_MENU)

    def update_checkboxes(self,new_status):
        i = 1
        self.block_changes = True
        for socket in new_status:
            menu_item = self.manager.get_widget('/Menubar/Menu/Socket' + str(i))
            if menu_item == None:
                continue
            menu_item.set_label("_%d: %s" % (i, socket[0]))
            menu_item.set_active(socket[1])
            i += 1
        self.block_changes = False

    def set_connected_ui(self):
        self.connected_mode = True
        menu = '''
            <ui>
             <menubar name="Menubar">
              <menu action="Menu">
               <menuitem action="Socket1"/>
               <menuitem action="Socket2"/>
               <menuitem action="Socket3"/>
               <menuitem action="Socket4"/>
               <separator/>
               <menuitem action="About"/>
               <separator/>
               <menuitem action="Quit"/>
              </menu>
             </menubar>
            </ui>
        '''
        actions = [
            ('Menu',  None, 'Menu'),
            #('Search', None, '_Search...', None, 'Search files with MetaTracker', self.on_activate),
            #('Preferences', gtk.STOCK_PREFERENCES, '_Preferences...', None, 'Change MetaTracker preferences', self.on_preferences),
            ('About', gtk.STOCK_ABOUT, '_About...', None, 'About NETIO-230A control', self.on_about),
            ('Quit', gtk.STOCK_QUIT, '_Quit', None, 'Quit the program', self.quit),]
        toggle_actions = [
            ('Socket1', None, '_1: Toggle Socket 1', None, 'Switch power socket 1 on or off.', self.on_toggle,True),
            ('Socket2', None, '_2: Toggle Socket 2', None, 'Switch power socket 2 on or off.', self.on_toggle),
            ('Socket3', None, '_3: Toggle Socket 3', None, 'Switch power socket 3 on or off.', self.on_toggle),
            ('Socket4', None, '_4: Toggle Socket 4', None, 'Switch power socket 4 on or off.', self.on_toggle),]
        ag = gtk.ActionGroup('Actions')
        ag.add_actions(actions)
        ag.add_toggle_actions(toggle_actions)
        self.manager = gtk.UIManager()
        self.manager.insert_action_group(ag, 0)
        self.manager.add_ui_from_string(menu)
        self.menu = self.manager.get_widget('/Menubar/Menu/About').props.parent
        #search = self.manager.get_widget('/Menubar/Menu/Search')
        #search.get_children()[0].set_markup('<b>_Search...</b>')
        #search.get_children()[0].set_use_underline(True)
        #search.get_children()[0].set_use_markup(True)
        #search.get_children()[1].set_from_stock(gtk.STOCK_FIND, gtk.ICON_SIZE_MENU)

    def quit(self, widget):
        if self.controller.deny_quit:
            self.controller.quit_requested()
        else:
            gtk.main_quit()

    def on_activate(self, data):
        #print("ok, here we want to toggle the visibility of the program...")
        self.controller.toggle_visibility()
    
    def on_toggle(self, action):
        if self.block_changes == True:
            return
        try:
            socket_name = action.get_name()
        except:
            raise NameError("actions seems to be no gtk.Action! something went wrong")
        if socket_name.find("Socket") != -1:
            try:
                #print("toggeling " + socket_name[6])
                self.controller.topical_window.netio.togglePowerSocketPower(int(socket_name[6]))
                #self.controller.topical_window.netio.disconnect()
                self.controller.topical_window.updatePowerSocketStatus()
            except:
                #print("sorry, log in first.")
                pass

    def on_popup_menu(self, status, button, time):
        if self.connected_mode:
            self.controller.topical_window.updatePowerSocketStatus()
        self.menu.popup(None, None, None, button, time)

    #def on_preferences(self, data):
    #    print 'preferences'

    def on_about(self, data):
        about = AboutDialog()
        about.run()



class Controller(object):
    def run(self):
        self.nextStep = "runDeviceSelector"
        self.visible = True
        self.deny_quit = False
        self.icon = TrayIcon(self)
        while self.nextStep != "":
            if self.nextStep == "runDeviceSelector":
                self.nextStep = ""
                self.runDeviceSelector()
            elif self.nextStep == "runDeviceController":
                self.nextStep = ""
                self.runDeviceController(self.nextStepKWArgs)
    
    def quit_requested(self):
        try:
            self.topical_window.dl.dialog.present()
        except:
            pass
    
    def toggle_visibility(self):
        if self.visible == True:
            self.topical_window.window.hide()
            self.visible = False
        else:
            self.topical_window.window.show()
            self.visible = True
    
    def setNextStep(self,what, **kwargs):
        self.nextStep = what
        self.nextStepKWArgs = kwargs
    
    def runDeviceSelector(self):
        self.icon.set_disconnected_ui()
        self.topical_window = DeviceSelector(self)
        gtk.main()
        del self.topical_window
    
    def runDeviceController(self, connection_details):
        self.icon.set_connected_ui()
        self.topical_window = DeviceController(self, connection_details)
        gtk.main()
        self.topical_window.timer_continue = False
        del self.topical_window.netio

def debug(message, level):
    if DEBUG_MODE and level > DEBUG_LEVEL:
        print(message)

def main():
    controller = Controller()
    controller.run()

if __name__ == "__main__":
    signal.signal(signal.SIGINT, signal.SIG_DFL) # ^C exits the application
    netio230a.TELNET_SOCKET_TIMEOUT = OVERWRITE_TELNET_SOCKET_TIMEOUT
    
    main()