This file is indexed.

/usr/share/hatari/hatariui/uihelpers.py is in hatari 1.7.0-1.

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

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
#!/usr/bin/env python
#
# Misc common helper classes and functions for the Hatari UI
#
# Copyright (C) 2008-2012 by Eero Tamminen
#
# 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.

import os
import sys
# use correct version of pygtk/gtk
import pygtk
pygtk.require('2.0')
import gtk
import gobject


# leak debugging
#import gc
#gc.set_debug(gc.DEBUG_UNCOLLECTABLE)


# ---------------------
# Hatari UI information

class UInfo:
    """singleton constants for the UI windows,
    one instance is needed to initialize these properly"""
    version = "v1.1"
    name = "Hatari UI"
    logo = "hatari.png"
    icon = "hatari-icon.png"
    copyright = "UI copyright (C) 2008-2012 by Eero Tamminen"

    # path to the directory where the called script resides
    path = os.path.dirname(sys.argv[0])
    
    def __init__(self, path = None):
        "UIinfo([path]), set suitable paths for resources from CWD and path"
        if path:
            self.path = path
        if not os.path.exists(UInfo.icon):
            UInfo.icon = self._get_path(UInfo.icon)
        if not os.path.exists(UInfo.logo):
            UInfo.logo = self._get_path(UInfo.logo)

    def _get_path(self, filename):
        sep = os.path.sep
        testpath = "%s%s%s" % (self.path, sep, filename)
        if os.path.exists(testpath):
            return testpath


# --------------------------------------------------------
# functions for showing HTML files

class UIHelp:
    def __init__(self):
        """determine HTML viewer and where docs are"""
        self._view = self.get_html_viewer()
        self._path = self.get_doc_path()

    def get_html_viewer(self):
        """return name of html viewer or None"""
        path = self.get_binary_path("xdg-open")
        if path:
            return path
        path = self.get_binary_path("firefox")
        if path:
            return path
        return None
        
    def get_binary_path(self, name):
        """return true if given binary is in path"""
        # could also try running the binary with "--version" arg
        # and check the exec return value
        if os.sys.platform == "win32":
            splitter = ';'
        else:
            splitter = ':'
        for i in os.environ['PATH'].split(splitter):
                fname = os.path.join(i, name)
                if os.access(fname, os.X_OK) and not os.path.isdir(fname):
                    return fname
        return None

    def get_doc_path(self):
        """return path or URL to Hatari docs or None"""
        # first try whether there are local Hatari docs in standard place
        # for this Hatari/UI version
        sep = os.sep
        path = self.get_binary_path("hatari")
        path = sep.join(path.split(sep)[:-2]) # remove "bin/hatari"
        path = path + sep + "share" + sep + "doc" + sep + "hatari" + sep
        if os.path.exists(path + "manual.html"):
            return path
        # if not, point to latest Hatari HG version docs
        print("WARNING: Hatari manual not found at:", path + "manual.html")
        return "http://hg.tuxfamily.org/mercurialroot/hatari/hatari/raw-file/tip/doc/"

    def set_mainwin(self, widget):
        self.mainwin = widget

    def view_url(self, url, name):
        """view given URL or file path, or error use 'name' as its name"""
        if self._view and "://" in url or os.path.exists(url):
            print("RUN: '%s' '%s'" % (self._view, url))
            os.spawnlp(os.P_NOWAIT, self._view, self._view, url)
            return
        if not self._view:
            msg = "Cannot view %s, HTML viewer missing" % name
        else:
            msg = "Cannot view %s,\n'%s' file is missing" % (name, url)
        from dialogs import ErrorDialog
        ErrorDialog(self.mainwin).run(msg)

    def view_hatari_manual(self, dummy=None):
        self.view_url(self._path + "manual.html", "Hatari manual")

    def view_hatari_compatibility(self, dummy=None):
        self.view_url(self._path + "compatibility.html", "Hatari compatibility list")

    def view_hatari_releasenotes(self, dummy=None):
        self.view_url(self._path + "release-notes.txt", "Hatari release notes")

    def view_hatari_todo(self, dummy=None):
        self.view_url(self._path + "todo.txt", "Hatari TODO items")

    def view_hatari_mails(self, dummy=None):
        self.view_url("http://hatari.tuxfamily.org/contact.html", "Hatari mailing lists")

    def view_hatari_repository(self, dummy=None):
        self.view_url("http://hg.tuxfamily.org/mercurialroot/hatari/hatari", "latest Hatari changes")

    def view_hatari_authors(self, dummy=None):
        self.view_url(self._path + "authors.txt", "Hatari authors")

    def view_hatari_page(self, dummy=None):
        self.view_url("http://hatari.tuxfamily.org/", "Hatari home page")

    def view_hatariui_page(self, dummy=None):
        self.view_url("http://koti.mbnet.fi/tammat/hatari/hatari-ui.shtml", "Hatari UI home page")


# --------------------------------------------------------
# auxiliary class+callback to be used with the PasteDialog

class HatariTextInsert:
    def __init__(self, hatari, text):
        self.index = 0
        self.text = text
        self.pressed = False
        self.hatari = hatari
        print("OUTPUT '%s'" % text)
        gobject.timeout_add(100, _text_insert_cb, self)

# callback to insert text object to Hatari character at the time
# (first key down, on next call up), at given interval
def _text_insert_cb(textobj):
    char = textobj.text[textobj.index]
    if char == ' ':
        # white space gets stripped, use scancode instead
        char = "57"
    if textobj.pressed:
        textobj.pressed = False
        textobj.hatari.insert_event("keyup %s" % char)
        textobj.index += 1
        if textobj.index >= len(textobj.text):
            del(textobj)
            return False
    else:
        textobj.pressed = True
        textobj.hatari.insert_event("keydown %s" % char)
    # call again
    return True


# ----------------------------
# helper functions for buttons

def create_button(label, cb, data = None):
    "create_button(label,cb[,data]) -> button widget"
    button = gtk.Button(label)
    if data == None:
        button.connect("clicked", cb)
    else:
        button.connect("clicked", cb, data)
    return button

def create_toolbutton(stock_id, cb, data = None):
    "create_toolbutton(stock_id,cb[,data]) -> toolbar button with stock icon+label"
    button = gtk.ToolButton(stock_id)
    if data == None:
        button.connect("clicked", cb)
    else:
        button.connect("clicked", cb, data)
    return button

def create_toggle(label, cb, data = None):
    "create_toggle(label,cb[,data]) -> toggle button widget"
    button = gtk.ToggleButton(label)
    if data == None:
        button.connect("toggled", cb)
    else:
        button.connect("toggled", cb, data)
    return button


# -----------------------------
# Table dialog helper functions

def create_table_dialog(parent, title, rows, cols, oktext = gtk.STOCK_APPLY):
    "create_table_dialog(parent,title,rows, cols, oktext) -> (table,dialog)"
    dialog = gtk.Dialog(title, parent,
        gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
        (oktext,  gtk.RESPONSE_APPLY,
        gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))

    table = gtk.Table(rows, cols)
    table.set_data("col_offset", 0)
    table.set_col_spacings(8)
    dialog.vbox.add(table)
    return (table, dialog)

def table_set_col_offset(table, offset):
    "set column offset for successive table_* ops on given table"
    table.set_data("col_offset", offset)

def table_add_entry_row(table, row, label, size = None):
    "table_add_entry_row(table,row,label,[entry size]) -> entry"
    # add given label to given row in given table
    # return entry for that line
    label = gtk.Label(label)
    align = gtk.Alignment(1) # right aligned
    align.add(label)
    col = table.get_data("col_offset")
    table.attach(align, col, col+1, row, row+1, gtk.FILL)
    col += 1
    if size:
        entry = gtk.Entry(size)
        entry.set_width_chars(size)
        align = gtk.Alignment(0) # left aligned (default is centered)
        align.add(entry)
        table.attach(align, col, col+1, row, row+1)
    else:
        entry = gtk.Entry()
        table.attach(entry, col, col+1, row, row+1)
    return entry

def table_add_widget_row(table, row, label, widget, fullspan = False):
    "table_add_widget_row(table,row,label,widget) -> widget"
    # add given label right aligned to given row in given table
    # add given widget to the right column and returns it
    # return entry for that line
    if fullspan:
        col = 0
    else:
        col = table.get_data("col_offset")
    if label:
        label = gtk.Label(label)
        align = gtk.Alignment(1)
        align.add(label)
        table.attach(align, col, col+1, row, row+1, gtk.FILL)
    if fullspan:
        col = table.get_data("col_offset")
        table.attach(widget, 1, col+2, row, row+1)
    else:
        table.attach(widget, col+1, col+2, row, row+1)
    return widget

def table_add_radio_rows(table, row, label, texts, cb = None):
    "table_add_radio_rows(table,row,label,texts[,cb]) -> [radios]"
    # - add given label right aligned to given row in given table
    # - create/add radio buttons with given texts to next row, set
    #   the one given as "active" as active and set 'cb' as their
    #   "toggled" callback handler
    # - return array or radiobuttons
    label = gtk.Label(label)
    align = gtk.Alignment(1)
    align.add(label)
    col = table.get_data("col_offset")
    table.attach(align, col, col+1, row, row+1, gtk.FILL)

    radios = []
    radio = None
    box = gtk.VBox()
    for text in texts:
        radio = gtk.RadioButton(radio, text)
        if cb:
            radio.connect("toggled", cb, text)
        radios.append(radio)
        box.add(radio)
    table.attach(box, col+1, col+2, row, row+1)
    return radios

def table_add_separator(table, row):
    "table_add_separator(table,row)"
    widget = gtk.HSeparator()
    endcol = table.get_data("n-columns")
    # separator for whole table width
    table.attach(widget, 0, endcol, row, row+1, gtk.FILL)


# -----------------------------
# File selection helpers

def get_open_filename(title, parent, path = None):
    buttons = (gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
    fsel = gtk.FileChooserDialog(title, parent, gtk.FILE_CHOOSER_ACTION_OPEN, buttons)
    fsel.set_local_only(True)
    if path:
        fsel.set_filename(path)
    if fsel.run() == gtk.RESPONSE_OK:
        filename = fsel.get_filename()
    else:
        filename = None
    fsel.destroy()
    return filename

def get_save_filename(title, parent, path = None):
    buttons = (gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
    fsel = gtk.FileChooserDialog(title, parent, gtk.FILE_CHOOSER_ACTION_SAVE, buttons)
    fsel.set_local_only(True)
    fsel.set_do_overwrite_confirmation(True)
    if path:
        fsel.set_filename(path)
        if not os.path.exists(path):
            # above set only folder, this is needed to set
            # the file name when the file doesn't exist
            fsel.set_current_name(os.path.basename(path))
    if fsel.run() == gtk.RESPONSE_OK:
        filename = fsel.get_filename()
    else:
        filename = None
    fsel.destroy()
    return filename


# File selection button with eject button
class FselAndEjectFactory:
    def __init__(self):
        pass

    def get(self, label, path, filename, action):
        "returns file selection button and box having that + eject button"
        fsel = gtk.FileChooserButton(label)
        # Hatari cannot access URIs
        fsel.set_local_only(True)
        fsel.set_width_chars(12)
        fsel.set_action(action)
        if filename:
            fsel.set_filename(filename)
        elif path:
            fsel.set_current_folder(path)
        eject = create_button("Eject", self._eject, fsel)

        box = gtk.HBox()
        box.pack_start(fsel)
        box.pack_start(eject, False, False)
        return (fsel, box)

    def _eject(self, widget, fsel):
        fsel.unselect_all()


# Gtk is braindead, there's no way to set a default filename
# for file chooser button unless it already exists
# - set_filename() works only for files that already exist
# - set_current_name() works only for SAVE action,
#   but file chooser button doesn't support that
# i.e. I had to do my own (less nice) container widget...
class FselEntry:
    def __init__(self, parent, validate = None, data = None):
        self._parent = parent
        self._validate = validate
        self._validate_data = data
        entry = gtk.Entry()
        entry.set_width_chars(12)
        entry.set_editable(False)
        hbox = gtk.HBox()
        hbox.add(entry)
        button = create_button("Select...", self._select_file_cb)
        hbox.pack_start(button, False, False)
        self._entry = entry
        self._hbox = hbox
    
    def _select_file_cb(self, widget):
        fname = self._entry.get_text()
        while True:
            fname = get_save_filename("Select file", self._parent, fname)
            if not fname:
                # assume cancel
                return
            if self._validate:
                # filename needs validation and is valid?
                if not self._validate(self._validate_data, fname):
                    continue
            self._entry.set_text(fname)
            return
    
    def set_filename(self, fname):
        self._entry.set_text(fname)
        
    def get_filename(self):
        return self._entry.get_text()

    def get_container(self):
        return self._hbox